2013-04-13 83 views
0

因爲它總是打印出'3'。不需要同步?我正在測試這個簡單的事情,因爲我在一個真正的多線程問題中遇到了麻煩,因爲它很大,所以不能很好地說明問題。這是展示這種情況的簡化版本。爲什麼這個線程安全?

class Test { 

public static int count = 0; 

class CountThread extends Thread { 

    public void run() 
    { 
     count++; 
    } 
} 

public void add(){ 
    CountThread a = new CountThread(); 
    CountThread b = new CountThread(); 
    CountThread c = new CountThread(); 

    a.start(); 
    b.start(); 
    c.start(); 

    try { 
     a.join(); 
     b.join(); 
     c.join(); 
    } catch (InterruptedException ex) { 
     ex.printStackTrace(); 
    } 

} 
public static void main(String[] args) { 

    Test test = new Test(); 
    System.out.println("START = " + Test.count); 

    test.add(); 

    System.out.println("END: Account balance = " + Test.count); 
} 
+1

圍繞'main'代碼循環,你會發現情況並非如此。 – Chan

回答

2

我看來,像count++足夠快結束,直到你調用「跑」到其它類。所以基本上它是連續運行的
但是,如果這是一個真實的例子,並且兩個不同的線程並行使用CountThread,那麼是的,你會遇到同步問題。

要驗證您可以嘗試在count ++之前和之後打印一些測試輸出,那麼在a.start()完成之前,您會看到b.start()是否調用count++。相同的c.start()

考慮使用的AtomicInteger代替,這是方式比同步更好可能時 -

incrementAndGet
public final int incrementAndGet()
以原子遞增一個的當前值。

+0

由於IO或PrintStream同步,打印將更改線程運行順序。證明你在說什麼很難。儘管它是最正確的。 – Gray

+0

如果我使用synchronized void run(),還需要使用AtomaticInteger()嗎? – user697911

+0

不,但您最好使用AtomicInteger,因爲它會產生更快的結果。 – danieln

0

僅僅因爲輸出是正確的,它不是線程安全的。創建一個線程會在操作系統上造成很大的開銷,之後只需要在單個時間片內完成這一行代碼。這不是線程安全的,只是沒有足夠的潛在衝突來觸發一個。

0

它不是線程安全的。

它恰好是短路的方式,以顯示該問題的可測量的機會。考慮在run中計數到更高的數字(1000000?)以增加在多個線程上重疊進行2次操作的機會。

另外,還要確保你的機器不是單核心CPU ...

4

,因爲它總是打印出「3」。不需要同步?

它不是線程安全的,你只是幸運。如果你運行這1000次,或在不同的架構上,你會看到不同的輸出 - 即不是3.

我建議使用AtomicInteger而不是一個靜態字段++而不是​​。

public static AtomicInteger count = new AtomicInteger(); 
... 
public void run() { 
    count.incrementAndGet(); 
} 
... 
+0

即使我使用AtomicInteger,我仍然必須使用靜態的,對嗎? – user697911

+1

您可以將計數器作爲「CountThread」的參數傳遞給線程。對於所有線程來說,'static'只是一個簡單的方法來引用同一個counter @ user697911。 – Gray

+1

-1會關心解釋答案的錯誤嗎? – Gray

1

此代碼是不是線程安全的:

public static int count = 0; 

class CountThread extends Thread { 

    public void run() 
    { 
     count++; 
    } 
} 

您可以在一個系統上運行該代碼上百萬次,它可能通過每次。這並不意味着它是線程安全的。

考慮將count中的值複製到多個處理器高速緩存的系統。它們都可能在強制其中一個緩存被複制回主RAM之前獨立更新。考慮到++不是原子操作。讀取和寫入count的順序可能會導致數據丟失。

實現這個代碼(使用Java 5及以上)的正確方法:

public static java.util.concurrent.atomic.AtomicInteger count = 
           new java.util.concurrent.atomic.AtomicInteger(); 

class CountThread extends Thread { 

    public void run() 
    { 
     count.incrementAndGet(); 
    } 
} 
0

使課堂線程要麼使數volatile強行線程之間的內存柵欄,或使用的AtomicInteger,或改寫像這樣(我的偏好):

class CountThread extends Thread { 

    private static final Object lock = new Object(); 

    public void run() 
    { 
     synchronized(lock) { 
      count++; 
     } 
    } 
}