关于 volatile 关键字的一些说明

A little clarification on the volatile keyword

考虑 Java 并发实践中的片段 -

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

对此 Whosebug thread 的评论的跟进

Actually, declaring the member field volatile still doesnt guarantee publication prior to holder being visible. You can look at ConcurrentHashMap's private method readUnderLock in which that takes this nuance into account. Though declaring holder as volatile does.

简而言之,他在此建议两件事-

public volatile Holder holder;

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

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

为什么这不会像作者建议的那样工作-

public class Holder {
private volatile int n;

declaring the member field volatile still doesnt guarantee publication prior to holder being visible

即使成员字段已被声明为可变的,它保证条件 n != n 将始终为 false,因此没有 AssertionError .请提出建议。

虽然使用 volatile 会像您建议的那样有所帮助,但在您可能碰巧尝试使用的许多其他情况下却无济于事。也就是说,很容易错误地使用 volatile,因此很难鼓励,除非你能确定你知道将使用哪些操作。

If a reference to an object is declared as volatile, does it ensures safe object publication?

这在很大程度上取决于操作的顺序和类型。在这种情况下,write

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

holder 的写入确保对 this.n 的写入必须可见,假设您也这样做

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

declaring the member field volatile still doesnt guarantee publication prior to holder being visible

我不同意这种情况。一旦您在另一个线程中获得了 holder,读取 n 就不可能看不到初始化的值,因为您正在读取一个 volatile 变量。这是因为 holder 仅在 n 初始化后设置。

试试这个

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

 }