简体   繁体   English

Java volatile变量是否会在读取之前强制执行之前的关系?

[英]Does Java volatile variables impose a happens-before relationship before it is read?

I have a piece of code which looks like this: 我有一段看起来像这样的代码:

Snippet A: 片段A:

class Creature {
    private static long numCreated;
    public Creature() {
        synchronized (Creature.class) {
            numCreated++;
        }
    }
    public static long numCreated() {
        return numCreated;
    }
}

From my understanding, since reading of numCreated is not synchronized, if Thread-A creates a Creature at 1pm, and Thread-B reads numCreated() at 2pm, numCreated() may well have returned either 0 or 1 (even when Thread-A has finished initializing the object at 1.05pm). 根据我的理解,由于numCreated读取不同步,如果Thread-A在下午1点创建一个Creature ,而Thread-B在下午2点读取numCreated() ,则numCreated()可能返回0或1(即使在Thread-A时)已经在晚上1点05分完成了对象的初始化。

So I added synchronized to numCreated() : 所以我添加了synchronizednumCreated()

Snippet B : 代码片段B

class Creature {
    private static long numCreated;
    public Creature() {
        synchronized (Creature.class) {
            numCreated++;
        }
    }
    public static synchronized long numCreated() { // add "synchronized"
        return numCreated;
    }
}

and all's well, except that I was thinking, if I modify it to Snippet C , is the variable numCreated still synchronized properly? 并且一切都很好,除了我在想,如果我将它修改为Snippet C ,变量numCreated仍然正确同步?

Snippet C: 片段C:

class Creature {
    private static volatile long numCreated; // add "volatile"
    public Creature() {
        synchronized (Creature.class) {
            numCreated++;
        }
    }
    public static long numCreated() { // remove "synchronized"
        return numCreated;
    }
}

With Snippet C , is it guaranteed that as soon as Thread-A completes object creation at 1:05pm, Thread-B's call to numCreated() is sure to return 1 ? 使用Snippet C ,可以保证一旦Thread-A在下午1:05完成对象创建,Thread-B对numCreated()的调用肯定会返回1吗?

PS: I understand that in a real situation I would probably use an AtomicLong but this is for learning purposes PS:我知道在实际情况下我可能会使用AtomicLong但这是出于学习目的

See http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility : 请参阅http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

A write to a volatile field happens-before every subsequent read of that same field. 在每次后续读取同一字段之前,会发生对易失性字段的写入。 Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking. 易失性字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定。

So the answer is yes. 所以答案是肯定的。 The write of the volatile in the constructor happens before the read of the volatile in numCreated() . 构造函数中volatile的写入发生在numCreated()读取volatile之前。 And since the non-atomic incrementation is still done in a synchronized block, the synchronization is alright (the incrementation is not atomic, but the write of the volatile long is). 并且由于非原子增量仍然在同步块中完成,因此同步是正常的(增量不是原子的,而是写入volatile的long)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM