关于volatile关键字的一点说明

时间:2016-08-20 11:09:00

标签: java volatile

考虑Java Concurrency in Practice中的片段 -

// 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;

我的问题是John Vint对此stackoverflow thread的评论的后续行动 -

  

实际上,声明成员字段不稳定仍然无法保证   在持有人可见之前发布。你可以看看   ConcurrentHashMap的私有方法readUnderLock   这种细微差别考虑在内。虽然声明持有者是不稳定的。

用简单的话说,他特此提出两件事 -

public volatile Holder holder;

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

上述解决方案是否能够完美运行?如果对象的引用被声明为volatile,它是否确保安全对象发布?数组也是对象。声明数组引用并不会使其元素线程安全。

为什么这不会像作者所建议的那样起作用 -

public class Holder {
private volatile int n;
  

声明成员字段volatile仍然无法保证   在持有人可见之前发布

即使成员字段已被声明为volatile,但它保证条件n != n将始终 false ,因此不会AssertionError。请建议。

2 个答案:

答案 0 :(得分: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;
  

声明成员字段volatile仍然不能保证在持有者可见之前发布

我不同意这种情况。一旦在另一个线程中获得holder,在读取volatile变量时,读取n将无法看到初始值。这是因为holder仅在n初始化后设置。

答案 1 :(得分: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;
    }

 }