考虑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
。请建议。
答案 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;
}
}