我偶尔会使用volatile
实例变量,在这种情况下,我有两个线程读取/写入它并且不希望获得锁定的开销(或潜在的死锁风险);例如,计时器线程定期更新在某个类上作为getter公开的int ID:
public class MyClass {
private volatile int id;
public MyClass() {
ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
execService.scheduleAtFixedRate(new Runnable() {
public void run() {
++id;
}
}, 0L, 30L, TimeUnit.SECONDS);
}
public int getId() {
return id;
}
}
我的问题:鉴于JLS只保证32位读取是原子的,永远使用volatile long有什么意义吗? (即64位)。
警告:请不要回复说使用volatile
而不是synchronized
是预先优化的情况;我很清楚如何/何时使用synchronized
,但有些情况下volatile
更可取。例如,在定义用于单线程应用程序的Spring bean时,我倾向于使用volatile
实例变量,因为无法保证Spring上下文将初始化主线程中的每个bean的属性。
答案 0 :(得分:121)
不确定我是否正确理解了您的问题,但JLS 8.3.1.4. volatile Fields说明了
字段可以声明为volatile,在这种情况下,Java内存模型可确保所有线程都看到变量的一致值(§17.4)。
,或许更重要的是,JLS 17.7 Non-atomic Treatment of double and long:
17.7非原子性处理双重和长期
[...]
出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两个单独的写入:每个32位半写一次。这可能导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二次32位的情况。 volatile和long和double值的写入和读取始终是原子的。对引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值。
也就是说,“整个”变量受volatile变体的保护,而不仅仅是两部分。这诱使我声称对long
s使用volatile比<{1}}更强更重要,因为甚至没有读取非易失性多头/双打的原子。
答案 1 :(得分:9)
这可以通过示例
来证明代码
public class VolatileTest {
private long foo;
private volatile long bar;
private static final long A = 0xffffffffffffffffl;
private static final long B = 0;
private int clock;
public VolatileTest() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
foo = clock % 2 == 0 ? A : B;
bar = clock % 2 == 0 ? A : B;
clock++;
}
}
}).start();
while (true) {
long fooRead = foo;
if (fooRead != A && fooRead != B) {
System.err.println("foo incomplete write " + Long.toHexString(fooRead));
}
long barRead = bar;
if (barRead != A && barRead != B) {
System.err.println("bar incomplete write " + Long.toHexString(barRead));
}
}
}
public static void main(String[] args) {
new VolatileTest();
}
}
输出
foo incomplete write ffffffff00000000
foo incomplete write ffffffff00000000
foo incomplete write ffffffff
foo incomplete write ffffffff00000000
请注意,这只适用于在32位虚拟机上运行时,在64位虚拟机上我几分钟内无法收到任何错误。
答案 2 :(得分:3)
&#34;挥发性&#34;有多种用途:
while (l != 0) {}
。还有更多吗?