线程安全/出版物

时间:2013-11-14 04:12:50

标签: java multithreading thread-safety

我一直在阅读并发行动,并提出了几个问题。

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上可见?

3 个答案:

答案 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并发性的一种简单方法如下:

  • 每个帖子都包含变量的副本
  • 如果未使用Java同步(volatile,locks,synchronized),则Thread可能永远不会“获取”变量的新副本,而在“set”的情况下,它可能永远不会将值刷新到main变量。

在您的示例中,您强制'setter'获取对象的锁定,并使用synchronized关键字将新值刷新到main变量:synchronized void addStoog

另一方面,你并没有强迫'getters'获得stooges的新副本,这将导致读取不一致的数据。