Java volatile变量是否会在读取之前强制执行之前的关系?
我有一段看起来像这样的代码:
片段A:
class Creature { private static long numCreated; public Creature() { synchronized (Creature.class) { numCreated++; } } public static long numCreated() { return numCreated; } }
根据我的理解,由于numCreated
读取不同步,如果Thread-A在下午1点创建一个Creature
,而Thread-B在下午2点读取numCreated()
,则numCreated()
可能返回0或1(即使在Thread-A时)已经在晚上1点05分完成了对象的初始化。
所以我添加了synchronized
到numCreated()
:
代码片段B :
class Creature { private static long numCreated; public Creature() { synchronized (Creature.class) { numCreated++; } } public static synchronized long numCreated() { // add "synchronized" return numCreated; } }
并且一切都很好,除了我在想,如果我将其修改为Snippet C ,变量numCreated
仍然正确同步?
片段C:
class Creature { private static volatile long numCreated; // add "volatile" public Creature() { synchronized (Creature.class) { numCreated++; } } public static long numCreated() { // remove "synchronized" return numCreated; } }
使用Snippet C ,可以保证一旦Thread-A在下午1:05完成对象创建,Thread-B对numCreated()
的调用肯定会返回1
吗?
PS:我知道在实际情况下我可能会使用AtomicLong
但这是出于学习目的
请参阅http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility :
对易失性字段的写入发生在每次后续读取该相同字段之前。 易失性字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定。
所以答案是肯定的。 构造函数中volatile的写入发生在numCreated()
读取volatile之前。 并且由于非primefaces增量仍然在同步块中完成,因此同步是正常的(增量不是primefaces的,而是写入volatile的长度)。