2013-04-06 28 views
0

我遇到了互聯網對以下Java類:與方法調用結合使用的Java多線程?

public class Lock1 implements Runnable { 
int b=100; 
public synchronized void m1() throws Exception { 
    b=1000; 
    Thread.sleep(50); 
    System.out.println("b="+b); 
} 

public synchronized void m2() throws Exception { 
    Thread.sleep(30); 
    //System.out.println("m2"); 
    b=2000; 
} 

public void run() { 
    try {m1();} 
    catch(Exception e) { 
     e.printStackTrace(); 
    } 
} 

public static void main(String[] args) throws Exception { 
    Lock1 tt=new Lock1(); 
    Thread t = new Thread(tt); 
    t.start(); 

    tt.m2(); 
    System.out.println(tt.b); 
} 
} 

試過這種跑了很多次,結果是幾乎總是:

 1000 
    b=1000 

在我原來的猜測,我認爲第一線應該是「2000」,因爲tt.m2()只是一個方法調用(不是線程),所以主方法應該繼續執行並獲得結果「b」,因爲方法m2中的值爲2000。

的第二次嘗試,我所做的就是取消註釋出

System.out.println("m2") 
在M2 method.Suprisingly

,其結果將是幾乎總是:

m2 
2000 
b=1000 

爲什麼在M2方法添加一條語句,是否會導致tt.b的輸出值發生變化?

對不起,我對這裏關於線程和方法調用之間的區別感到很困惑,希望高手能幫幫忙!

+0

儘量讓'B'揮發性 – 2013-04-06 15:08:13

回答

5

在Java意義上的同步結合了幾件事情。在這種情況下,這些點是有趣:

  • 互斥
  • 讀者記憶障礙
  • 作家

記憶障礙後進入​​塊(或方法),你有兩個確保:您有鎖(互斥),並且JVM和編譯器將丟棄同步對象的的任何緩存。這意味着對this.b的訪問將從RAM獲取「b」的實際值,而不是從任何緩存獲取實際值,但僅獲取的一個值。然後它將再次與緩存副本一起工作。

離開又一個​​塊保證了CPU刷新所有髒(即寫)緩存到存儲器。

在你的東西的一點是:System.out.println(tt.b);絕非同步,這意味着獲得它並沒有越過定義的內存屏障。因此,儘管另一個線程爲b寫入了一個新值並將其刷新到RAM,但主線程並不知道它應該從RAM讀取b,而不是從它自己的緩存中讀取b

解決的辦法是:

synchronized(tt){ 
    System.out.println(tt.b); 
} 

這符合黃金法則,即如果事情是同步的則訪問它應該是同步的,並不僅是訪問的一半。

和關於讓您System.out:有三件事情:

第一:它是緩慢的(相對於一些內存擺弄)。這意味着在此期間CPU或JVM可能會自己決定,tt的新外觀可能是合適的

第二:它很大(與某些內存小提琴相比)。這意味着單獨觸摸的代碼可能會從緩存中驅逐tt

第三:它在內部同步。這意味着你跨越了一些記憶障礙(這可能與你的tt無關) - 誰知道)。但這些也可能有一些效果。

這是多線程調試的領先法則:爲了趕會錯誤添加System.out,根據墨菲,其實隱藏的問題。

+1

+1。一個答案! – 2013-04-06 15:29:37

+0

tt.m2()在主同步? – Kevin 2013-04-06 15:51:32

+0

@Kevin'm2()'的主體將被同步執行。 – 2013-04-06 15:53:00

1

我想這是特定於JVM實現。

基本上,每個線程都有對象變量其自身的副本(視圖)和他們同步來回沒有確定的方式。

+0

怎麼會是這樣的JVM實現特定的?我不同意每個線程都有自己的「副本」。相反,他們有SAME對象。 「意外」結果的原因是由於延遲('Thread.sleep')和打開輸出流('System.out.println')的開銷之間的競爭條件。 – 2013-04-06 15:24:14

+1

@czarpino:有一個競爭條件,並且還有一個可見性問題。每個線程都可以擁有自己的變量值副本。閱讀http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html的最後一部分。 – 2013-04-06 15:26:44

+0

@JBNizet啊,是的,你的權利!我誤解/誤解了。我認爲維塔利意味着物體本身。儘管如此,我仍然認爲比賽條件是相關問題。 – 2013-04-06 15:33:17

1

最可能的原因是System.out.println很慢。 「意外」結果的原因是由於延遲(Thread.sleep)與打開輸出流(System.out.println)的開銷之間的競爭狀態。