如果从2个线程写入volatile变量会发生什么?

时间:2016-08-05 16:12:43

标签: java multithreading volatile

考虑 Java Concurrency in Practice -

中的代码片段
@ThreadSafe
public class SynchronizedInteger{
    @GuardedBy("this") private int value;

    public synchronized int getValue() {
        return value;
    }

    public synchronized void setValue(int value) {
        this.value = value;
    }
}

同一本书的摘录 -

  

考虑易变量的好方法是想象它们   表现与上面列出的SynchronizedInteger类大致相同,   通过调用get来替换volatile变量的读写   并设定。然而,访问volatile变量不执行锁定等等   不能导致执行线程阻塞,从而产生volatile变量   一种比同步更轻的同步机制。

线程限制的特例适用于volatile变量。只要确保 volatile变量仅从单个线程写入,就可以安全地对共享的volatile变量执行读 - 修改 - 写操作。

所以,如果你将上面的类中的实例变量设为volatile,然后删除synchronized关键字,那么假设有3个线程

线程A&线程B写入相同的volatile变量 线程C读取volatile变量。

由于volatile变量现在是从2个线程写入的,为什么对这个共享的volatile变量执行读 - 修改 - 写操作是不安全的?

4 个答案:

答案 0 :(得分:2)

这是因为对volatile变量的读 - 修改 - 写操作不是原子的。 v++实际上就像是:

r1 = v;
r2 = r1 + 1;
v = r2;

因此,如果您有两个线程执行此操作,则可能导致变量仅增加一次,因为它们都读取旧值。这就是为什么它不安全的一个例子。

在你的例子中,如果你删除了synchronized,将字段设置为volatile并且在基于getValue返回的某些条件逻辑之后有两个线程调用setValue将是不安全的 - 该值可能已由另一个线程修改。

如果您想要原子操作,请查看java.util.concurrent.atomic包。

答案 1 :(得分:2)

关键字volatile用于确保其他Object可以看到对Thread的更改。 这并不强制执行Object上的非原子操作,在操作完成之前不会发生其他Thread干扰。 要执行此操作,您需要使用关键字synchronized

答案 2 :(得分:1)

如果在不使用任何同步构造的情况下从多个线程编写volatile变量,则必然会出现数据不一致错误。

在单个写入线程和多个读取线程用于原子操作的情况下,使用volatile变量而不进行同步。

易失性确保从主内存而不是线程缓存中获取变量值。在单次写入和多次读取操作的情况下使用是安全的。

使用原子变量或同步或Lock API来更新和读取多个线程中的变量。

参考相关的SE问题:

What is meant by "thread-safe" code?

答案 3 :(得分:1)

如果两个线程正在写入而没有先读取变量,则没有问题..这是安全的。如果线程先读取,然后修改然后写入,则会出现问题。如果第二个线程也同时读取,读取与第一个线程相同的旧值,并对其进行修改,该怎么办?。当它写入时,它将简单地覆盖第一个线程的更新。 BOOM。

val i = 1 ->线程读取1->线程2读取1->线程1进行1 * .2 = 1.2->线程2进行1 * .3 = 1.3->线程1回写1.2->线程2 cooly将其覆盖为1.3做1.2 * .3