如何解决多线程静态变量自增?

Mak*_*dor 0 java multithreading volatile increment

所以我的问题本质上是,即使我使用 static 易失性 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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

我的问题出在函数 go() 中。我对元素进行编号,我有多个 Producer 对象作为独立线程运行,但有时它们表现得好像不知道 n 是否已更新,所以我得到相同的索引。有任何想法吗?(我知道可能是什么问题,但我不知道如何解决它。)

Tur*_*g85 5

似乎对什么有误解volatile。该关键字volatile在写入和读取之间引入了happens-before语义。然而,它并不使多个操作原子化。

如果我们要编写“手动”的语义n++(请永远不要这样做,它仅用于解释目的),它看起来像这样:

final int result;
n = (result = n) + 1;
Run Code Online (Sandbox Code Playgroud)

Ideone demo

看看这段代码,我们发现我们必须:

  1. 读取 的值n
  2. 将其存储在某个临时变量中result
  3. 将其增加1, 和
  4. 将(增加的)值写回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();
                ...
        }
    }

}
Run Code Online (Sandbox Code Playgroud)