所以我的問題本質上是,即使我使用靜態 volatile int 變數進行增量,我的一些資料也不會保持唯一,這將是我的目標(我給我的元素編號)。
public class Producer implements Runnable{
private String str;
private Fifo f;
private int i;
private static volatile int n=0;
public Producer(String str,int i,Fifo f) ....
public void run() {
try {
this.go();
} catch (InterruptedException e) {
;
}
}
void go() throws InterruptedException {
while(true) {
Thread.sleep(i);
int l=n ;
String k=str " " l " ";
f.put(k);
System.out.println("produced " str " " l " " System.currentTimeMillis()%100000);
}
}
}
我的問題出在函式 go() 中。我給我的元素編號,我有多個 Producer 物件作為獨立執行緒運行,但有時它們表現得好像不知道 n 是否已更新,所以我得到相同的索引。有任何想法嗎?(我知道可能是什么問題,但我不知道如何解決它。)
uj5u.com熱心網友回復:
似乎對做什么有誤解volatile。該關鍵字volatile在寫入和讀取之間引入了happens-before 語意。但是,它不會使多個操作原子化。
如果我們要撰寫n “手工”的語意(請永遠不要這樣做,它僅用于說明目的),它看起來像這樣:
final int result;
n = (result = n) 1;
Ideone demo
查看這段代碼,我們看到我們必須:
- 讀取 的值
n, - 將其存盤在某個臨時變數中
result, - 將其增加
1, 和 - 將(增加的)值寫回
n
所以我們有多個操作。如果這些操作由不同的執行緒并行執行多次,那么我們可以看到多種可能的交織導致資料不一致。例如,兩個執行緒都可以讀取 的(當前)值n。兩者都會將值加一,并且都會將新值寫回n. 這意味著兩個執行緒已經執行了“增量”,但是 的值n只增加了1而不是2。
我們可以使用專門的類——在這種情況下AtomicInteger——來避免這個問題。用法如下所示:
public class Producer implements Runnable {
...
private static final AtomicInteger n = new AtomicInteger(0);
...
void go() throws InterruptedException {
while(true) {
...
int l = n.getAndIncrement();
...
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/349083.html
