有没有使用一个易变的长期?

我偶尔使用一个volatilevariables来处理两个线程读取/写入的情况,而不希望取出锁的开销(或潜在的死锁风险)。 例如一个定时器线程定期更新一个inttypes的ID,这个ID在某些类上被作为getter使用:

 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位读取将是primefaces是有没有使用一个易变的长期的任何一点? (即64位)。

警告 :请不要回复说,使用volatile over synchronized是一种预优化的情况; 我很清楚如何/何时使用synchronized但有些情况下volatile是最好的。 例如,当定义一个在单线程应用程序中使用的Spring bean时,我倾向于支持volatilevariables,因为不能保证Spring上下文将初始化主线程中的每个bean的属性。

不知道我是否正确理解你的问题,但是JLS 8.3.1.4。 易变的字段说:

一个字段可以被声明为volatile,在这种情况下,Java内存模型可以确保所有的线程都能看到一个一致的variables值( §17.4 )。

也许更重要的是, JLS 17.7双primefaces和非primefaces的非primefaces处理 :

17.7双primefaces和长子的非primefaces处理
[…]
对于Java编程语言内存模型而言,对非易失性long或double值的单个写入被视为两个独立的写入:每个32位一半。 这可能会导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二个32位。 写入和读取volatile和double值始终是primefaces的。 写入和读取引用始终是primefaces的,无论它们是以32还是64位值实现的。

也就是说,“整个”variables由volatile修饰符保护,而不仅仅是两个部分。 这诱使我声称使用volatile作为long s比使用int s 更重要 ,因为即使对于non-volatile longs / double, 读取也不是primefaces的。

这可以通过示例来certificate

  • 不断地切换两个字段,一个标记为易失性,一个不在所有位设置和所有位清零
  • 读取另一个线程上的字段值
  • 看到foo字段(不受volatile保护)可以在不一致的状态下读取,这种情况绝不会发生在带有volatile的bar字段上

 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位虚拟机上,我几分钟内无法获得一个单独的错误。

“易变”有多种用途:

  • 保证primefaces写入加倍/长
  • 保证了当线程A看到由线程B产生的volatilevariables的变化时,线程A还可以看到线程B在更改volatilevariables之前所做的所有其他更改(想想在设置单元格本身之后设置数组中使用的单元格的数量) 。
  • 基于假定只有一个线程可以改变这个variables来防止编译器优化( while (l != 0) {}考虑紧密的循环while (l != 0) {}

还有更多吗?