我一直在阅读并发行动,并提出了几个问题。
public final class ThreeStooges {
private final Set<String> stooges = new HashSet<String>();
public ThreeStooges() {
stooges.add("Moe");
stooges.add("Larry");
stooges.add("Curly");
}
public boolean isStooge(String name) {
return stooges.contains(name);
}
}
这本书说,由于这个类是不可变的,所以它是线程安全的,因为没有修改状态(stooges)。 我很困惑的是这个。如果多个线程同时调用isStooge(String name)方法会怎样。怎么了?
public class HolderObject{
private 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.");
}
}
与此相同。它出什么问题了?如果多个线程调用assertSanity()
怎么办?
谢谢你们
更新
假设stooges类更改为以下内容...
public class ThreeStooges {
private List<String> stooges = new ArrayList<String>();
public ThreeStooges() {
stooges.add("Moe");
stooges.add("Larry");
stooges.add("Curly");
}
public synchronized void addStoog(String stoog){
stooges.add(stoog);
}
public boolean getStoog(int index){
return stooges.get(index);
}
public boolean isStooge(String name) {
return stooges.contains(name);
}
}
这里有线程问题吗?吸气剂的可见性问题? 如果线程A是addStoog(“Bobby”)而线程B调用getStoog(3),那么最终的stoog是否会在getter上可见?
答案 0 :(得分:2)
如果多个线程调用isStooge(String name)方法,该怎么办? 同时。会发生什么?
在Java的内存模型中,如果两个线程同时访问相同的数据并且其中至少有一个是写入,则会遇到麻烦。这称为conflicting access。
在这个例子中,你有多个线程访问相同的数据(stooges
变量),但没有一个修改数据,所以你没事。
这本书说这不是线程安全的吗?为什么?它没有正确发布是什么意思?
但是,在这种情况下,您为holder
分配一个新值,这是一个写入。这是一场数据竞争,如果两个线程在没有外部同步的情况下同时调用initialize
,则会发生不好的事情。
术语“已发布”最有可能是指一个线程中所做的更改如何对其他线程可见。虽然我不认为这是一个常用术语,但我想这本书应该在某个时候给出一个确切的术语定义。
与此相同。它出什么问题了?如果多个怎么办? 线程调用assertSanity()。
正如您发布的那样,代码似乎很好。由于assertSanity
只读取n
,因此您没有数据竞争。
然而,正如@ EnnoShioji的回答所指出的那样,线程可能会出现n
的未初始化值,因此看似琐碎的检查可能会失败。
答案 1 :(得分:1)
我同意@ComicSansMS的前两个答案,但我认为他的第三个答案并没有解决这本书的原始观点。
本书的这一部分(Java Concurrency in Practice)讨论的是安全发布。一个不可变的对象并不意味着它没有并发问题(在这种情况下, visibility 问题)。即使Holder
的状态在其生命周期内没有改变,如果它在不满足某些规则的情况下由不同的线程访问,则另一个线程可能会在构造中看到一个对象,因此看到不正确的值。
因为在构造对象时,首先填充字段为null / 0,在第三个示例(Holder
)中,线程可以看到n=0
然后n=42
,因此抛出AssertionError
。
这本书的含义是:
更糟糕的是,其他线程可以看到最新值 对于持有人参考,但状态的陈旧值 持有人
如果你已经将int字段声明为final
,那么Holder对象将成为一个正式的&#34;不可变对象&#34;因此JMM可以保证不会发生这样的竞争条件(参见关于不可变对象的特殊保证的段落)。本书还介绍了可以遵循的其他规则,以防止这种情况发生。
答案 2 :(得分:0)
考虑Java并发性的一种简单方法如下:
在您的示例中,您强制'setter'获取对象的锁定,并使用synchronized
关键字将新值刷新到main变量:synchronized void addStoog
另一方面,你并没有强迫'getters'获得stooges
的新副本,这将导致读取不一致的数据。