考虑 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变量执行读 - 修改 - 写操作是不安全的?
答案 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问题:
答案 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