2012-04-09 13 views
3

爲了瞭解通過Java進行同步的工作,我只是簡單地介紹一些簡單的事情,比如創建一個在線程間共享的計數器。使用AtomicInteger作爲靜態共享計數器

我遇到的問題是我無法弄清楚如何在100%的時間內連續打印計數器。

int counterValue = this.counter.incrementAndGet(); 
System.out.println(this.threadName + ": " + counterValue); 

以上的增量AtomicInteger counter,獲取新的價值,並打印到由負責該更新線程名稱標識的控制檯。當看到incrementAndGet()方法導致JVM在打印當前線程的更新值之前上下文切換到另一個線程進行更新時,會出現問題。這意味着在線程返回到執行狀態之前,該值會遞增但不會打印。看着這個例子的輸出時,這是顯而易見的:

Thread 3: 4034 
Thread 3: 4035 
Thread 3: 4036 
Thread 1: 3944 
Thread 1: 4037 
Thread 1: 4039 
Thread 1: 4040 
Thread 2: 3863 
Thread 1: 4041 
Thread 1: 4043 

你可以看到,當執行返回到線程1,它打印的價值,並繼續更新。線程2也是如此。

我有一種感覺,我錯過了一些非常明顯的東西。

回答

7

當它出現的incrementAndGet()方法會引起JVM在上下文切換到另一個線程其更新打印當前線程的更新前值

這是一個競爭條件出現的問題是在這些情況下往往會發生。雖然AtomicInteger計數器被正確遞增,有什麼可從增量發生,之前被稱爲println之後被交換停止Thread 2

int counterValue = this.counter.incrementAndGet(); 
// there is nothing stopping a context switch here 
System.out.println(this.threadName + ": " + counterValue); 

如果您想要打印的「反順序100%的時間」,你將不得不對兩左右的增量println呼叫鎖定同步。當然,如果你這樣做,那麼浪費了AtomicInteger

synchronized (counter) { 
    System.out.println(this.threadName + ": " + counter.incrementAndGet()); 
} 

如果你爲什麼您需要的輸出是連續的,也許有更好的解決方案,沒有這種競爭狀態編輯您的問題來解釋

+0

沒有特別的原因,我想除了我以外的其他順序輸出,只是爲了學習目的而混淆不同的同步場景。我想我現在也終於明白了synchronized塊。非常感謝。 – 2012-04-09 18:43:34

+0

我認爲@ jskiles1。競爭條件可能會非常棘手和令人困惑。那裏有一堆很好的教程。這裏看起來不錯。祝你好運。 http://www.vogella.de/articles/JavaConcurrency/article.html – Gray 2012-04-09 18:48:51

1

您需要整個建築爲同步:

synchronized(this) {  
    int counterValue = this.counter.incrementAndGet(); 
    System.out.println(this.threadName + ": " + counterValue); 
} 

在這種情況下,雖然你沒有使用的AtomicInteger。平原int將工作(計數器++)。

1

要順序打印,incAndGet和println必須位於關鍵區域,只有一個線程可以輸入一段代碼,其他線程被阻止。例如,可以用二進制信號量來實現,如java​​。

你可以把東西放在頭上,讓一個線程增加計數器並打印它。其他的線程可能在一個「關鍵區域」中只能一個接一個地接着一個計數器。這樣會更有效率,因爲關鍵區域應該保持較小,最好不要I/O。

+0

與您的觀點相關的代碼示例可能有幫助。 – 2016-06-03 05:01:08