如何使volatile count操作成为线程安全的

prv*_*rvn 3 java multithreading volatile

我一直在经历JCIP,作者在那里说..

线程限制的特殊情况适用于volatile变量.只要确保volatile变量仅从单个线程写入,对共享的volatile变量执行读 - 修改 - 写操作是安全的.

例如,count ++被认为是一个复合操作(读取值,向其中添加一个值,并更新值)并将count指定为volatile不会使此操作成为原子操作,因此这里不保证线程安全!我对吗 ??但是这里作者说我们可以修复它,如果我们确保volatile变量只是从一个线程写的.我没理解这一点.请提供说明.

ant*_*wrx 6

count ++本质上是两个操作,读取count和store count + 1的值.

如果两个线程同时尝试计数++,它们可能都会读取旧的count值并将其递增1.因此,最后,如果您不添加任何同步机制,最终将得到count + 1的最终值,而不是count + 2.

Volatile不保证在这种情况下(非原子)线程安全,它只保证最近存储的值可供所有线程使用.但这对上述情况没有多大帮助.您需要确保在存储新的线程之前,没有其他线程会读取旧的count值.

如果你需要有线程安全计数器,我建议你看一下AtomicInteger(和类似的)类.它们提供count ++的原子版本; incrementAndGet方法.


biz*_*lop 6

典型的用例可能如下所示:

public class Foo implements Runnable {
    private volatile int itemsProcessed;

    public int getItemsProcessed() { return itemsProcessed; }

    @Override
    public void run() {
        while (true) { //this is just an example so we don't care about stopping
            processItem(); //process a single item
            itemsProcessed++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在其他人可以在没有额外同步的情况下查询线程的进度,但只允许线程本身更新它.对于其他人来说,该字段是只读的.

如果没有volatile其他线程可能根本看不到任何变化itemProcessed,或者甚至可能以古怪的顺序看到它们,有时会增加,有时会减少.


只有volatile写入是幂等的(即多次写入与单个写入具有相同的效果),多次线程可以在没有任何额外同步的情况下写入变量的唯一时间.这可以在用于停止线程的这种模式中看到:

public class Foo implements Runnable {
    private volatile boolean stopped = false;

    public void stopProcessing() {
        stopped = true;
    }

    public int getItemsProcessed() { return itemsProcessed; }

    @Override
    public void run() {
        while (!stopped) { 
            processItem(); //process a single item
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其他线程可以做的唯一事情是设置stopped为true.无论有多少人这样做,无论顺序如何,结果总是一样的.