所以我的问题本质上是,即使我使用静态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是否被更新了,所以我得到了相同的索引。什么好主意吗?(我知道可能是什么问题,但我不知道如何解决它。)
对于volatile
的作用似乎存在误解。关键字volatile
在写和读之间引入了happens-before语义。但是,它不能使多个操作原子化。
如果我们要"手写"n++
的语义;(请不要这样做,这只是为了说明目的),它看起来像这样:
final int result;
n = (result = n) + 1;
<<p>Ideone演示/kbd>看一下这段代码,我们看到我们必须:
- 读取
n
值, - 将其存储在某个临时变量
result
中, - 增加
1
, - 将(递增的)值写回
n
所以我们有多个操作。如果这些操作由不同的线程并行执行多次,那么我们可以看到大量可能导致数据不一致的交织。例如,两个线程都可以读取n
的(当前)值。两者都将值增加1,并将新值写回n
。这意味着两个线程执行了"increment",但是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();
...
}
}
}