Java易失性变量在读取之前强加一个发生先前的关系吗? [英] Does Java volatile variables impose a happens-before relationship before it is read?

查看:131
本文介绍了Java易失性变量在读取之前强加一个发生先前的关系吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段代码看起来像这样:



代码段A:

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



从我的理解, c> numCreated 不同步,如果Thread-A在下午1点创建 Creature ,并且Thread-B读取 numCreated )在下午2点, numCreated()可能返回0或1(即使线程A已经完成初始化对象在1.05pm) 。



所以我添加了同步 numCreated()



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

一切正常,除了我在想,if我将其修改为 Snippet C ,是 numCreated 仍正确同步?



Snippet C:

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

使用它保证一旦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之前发生。由于非原子递增仍然在同步块中完成,所以同步是正确的(递增不是原子的,而是volatile long的写入)。


I have a piece of code which looks like this:

Snippet 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).

So I added synchronized to numCreated():

Snippet 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:

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 ?

PS: I understand that in a real situation I would probably use an AtomicLong but this is for learning purposes

解决方案

See 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(). 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).

这篇关于Java易失性变量在读取之前强加一个发生先前的关系吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆