如果我使用非最终的ConcurrentHashMap会发生什么

时间:2013-11-07 05:42:11

标签: java multithreading concurrenthashmap

我在某处读到,即使ConcurrentHashMap在多个线程中使用是安全的,也应该声明为final,甚至是private final。我的问题如下:

1)CocurrentHashMap是否仍保持线程安全而不将其声明为final

2)关于私有关键字的相同问题。可能最好问一般问题 - public/private关键字会影响运行时行为吗?我理解它们在内部/外部类的可见性/用法方面的含义,但在多线程运行时的上下文中意味着什么呢?我认为像public ConcurrentHashMap这样的代码可能只在编码样式术语中不正确,而不是在运行时,我是对的吗?

2 个答案:

答案 0 :(得分:5)

在评论中提供我所谈论的更具体的例子可能会有所帮助。假设我做的是这样的事情:

public class CHMHolder {

    private /*non-final*/ CHMHolder instance;

    public static CHMHolder getInstance() {
        if (instance == null) {
            instance = new CHMHolder();
        }
        return instance;
    }

    private ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

    public ConcurrentHashMap<String, String> getMap() {
        return map;
    }
}

现在,由于一大堆原因,这不是线程安全的!但是,假设threadA看到null的{​​{1}}值,从而实例化instance,然后通过一个愉快的巧合,threadB看到相同的CHMHolder实例(这是不保证,因为没有同步)。您会认为threadB看到非CHMHolder null,对吧?它可能没有,因为在threadA的CHMHolder.map和threadB的map = new ...之间没有正式的发生边缘。

这在实践中意味着像return map这样的东西可能会引发CHMHolder.getInstance().getMap().isEmpty(),这会让人感到困惑 - 毕竟,NullPointerException看起来应该总是返回非getInstance 1}} nullCHMHolder看起来总是应该有非CHMHolder地图。啊,多线程的乐趣!

如果null被标记为map,那么user2864740引用的JLS位适用。这意味着如果threadB看到了threadA看到的同一个实例(同样,它可能没有),那么它也会看到final所做的map = new...动作 - 也就是说,它会看到非threadA CHM实例。一旦看到,CHM的内部线程安全就足以确保安全访问。

答案 1 :(得分:3)

finalprivate对所述变量命名的对象thread-safety(或缺少)没有任何说明。 (他们修改变量,而不是对象。)无论如何..


变量如果是final field,则会在各个主题中保持一致:

  

当构造函数完成时,对象被认为是完全初始化的。在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的final字段的正确初始化值。

实际的ConcurrentHashMap 对象是“线程安全的”,只要其保证。特别是,只保证单个方法调用/操作,因此可能需要使用更大的同步代码。如果只能从创建它的对象访问CHM,则可以轻松控制。

使用private通常被认为是好的,因为它阻止其他代码“意外地”访问变量(以及它所命名的对象)。但是,私有修饰符建立与final修饰符相同的发生前保证,因此与线程安全性正交。