2016-08-20 50 views
0

考虑从Java并发的片段在行为─一点澄清volatile关键字

// Unsafe publication 
public Holder holder; 

public void initialize(){ 
    holder = new holder(42); 
} 

public class Holder{ 
    private int n; 

    public Holder(int n) { 
     this.n = n; 
    } 

    public void assertSanity(){ 
     if (n != n) 
      throw new AssertionError("This statement is false."); 
    } 
} 

一经书的作者提出的解决方案是 -

public static Holder holder = new Holder(42); 

如果唯一的要求是防止AssertionError,那么这也可以很好地工作 -

private final int n; 

我的问题是后续的评论对这段计算器thread约翰Vint-

其实,宣告成员字段挥发性仍犯规保证 出版物持有者可见之前。您可以看看 ConcurrentHashMap的私有方法readUnderLock,其中考虑到了这个细微差别 。虽然宣称持有人是挥发性的。

说得简单的话,他特此提示2东西 -

public volatile Holder holder; 

public void initialize(){ 
    holder = new holder(42); 
} 

在上述解决方案将很好地工作?如果将引用声明为volatile,是否确保安全对象发布?数组也是对象。声明数组引用不会使其元素线程安全。

而且为什么由作者 -

public class Holder { 
private volatile int n; 

的建议,宣布成员字段挥发性仍犯规保障 公布持有人前,这是不行的可见

即使作为成员字段已被声明为volatile,其保证条件n != n将始终为false,因此没有AssertionError。请建议。

回答

2

虽然使用volatile会帮助您建议,但它不会帮助您可能碰巧尝试使用的许多其他情况。即错误地使用volatile非常容易,所以除非您确定知道哪些操作将被使用,否则很难鼓励。

如果对一个对象的引用声明为volatile,它是否确保安全对象发布?

这非常依赖于操作的顺序和类型。在这种情况下,写

this.n = n; 
// and then 
holder = ... 

holder写确保在写this.n必须是可见假设你还做

Holder h = holder; // read barrier 
// n must be set. 
int n = this.n; 

声明的成员字段之前持有挥发性仍犯规保证出版物可见

我不同意这种情况。一旦在另一个线程中获得了holder,读取n将不会在您读取volatile变量时看到初始化的值。这是因为只有在n被初始化后才设置holder

+0

感谢。但是这个具体案例不是作者错误的。 –

+0

@ShirgillFarhanAnsari我同意,一般来说'volatile'不提供你需要的保证,但在这种特定情况下,它会发生作用。 –

+0

再次感谢,但没有得到您的第二个代码片段持有人h =持有人;//读屏障的观点。请详细说明。 –

0

试试这个

public class main { 
public volatile static Holder holder; 
public static void main(String[] args) { 
    // TODO Auto-generated method stub 
    // Unsafe publication 
    initialize(); 
    holder.assertSanity(99); 
} 
public static void initialize(){ 
    holder = new Holder(42); 
} 
} 

Holder.java

public class Holder { 

private volatile Integer n; 
    public Holder(Integer n) { 
     this.n = n; 
    } 

    public void assertSanity(Integer n){ 
     if (!((Object)this.n).equals((Object)n)) 
      throw new AssertionError("This statement is false."); 
    } 

    public Integer getN() { 
     return n; 
    } 

    public void setN(Integer n) { 
     this.n = n; 
    } 

}