5

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

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

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

4

2 回答 2

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 看到 的nullinstance并因此实例化CHMHolder,然后 threadB 巧合地看到了同一个CHMHolder实例(这不能保证,因为没有同步)。您会认为 threadB 看到的是非null CHMHolder.map- ,对吧?可能不会,因为在 threadA'smap = new ...和 threadB's之间没有正式的发生前边缘return map

这在实践中意味着类似的东西CHMHolder.getInstance().getMap().isEmpty()可能会抛出一个NullPointerException,这会令人困惑——毕竟,getInstance看起来它应该总是返回一个非null CHMHolder,而且CHMHolder看起来它应该总是有一个非null映射。啊,多线程的乐趣!

如果map被标记final,则 user2864740 引用的 JLS 位适用。这意味着如果 threadB 看到与 threadA 看到的实例相同的实例(同样,它可能不会看到),那么它也会看到执行的map = new...操作threadA——也就是说,它会看到非nullCHM 实例。一旦看到这一点,CHM 的内线程安全将足以确保安全访问。

于 2013-11-07T06:27:39.227 回答
3

finalprivate且不说由所述变量命名的对象的线程安全性(或缺乏线程安全性) 。(他们修改变量,而不是对象。)无论如何..


如果变量是最终字段,则该变量将在线程之间保持一致:

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

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

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

于 2013-11-07T06:00:24.257 回答