放入j.u.c.ConcurrentHashMap后ArrayList会安全发布吗?

Will the ArrayList be published safely after putting into j.u.c.ConcurrentHashMap?

我对 ArrayList 的安全发布有些怀疑。

我有代码:

private final Map<Long, SomeStuff> map = new ConcurrentHashMap<>();
//Called by Thread 1
public void write() {
   List list = new ArrayList<>();
   for (int i = 0; i <100 ; i++) {
      list.add(new SomeStuff(i))
   }
   map.put(1L,list)
}
// Called by Thread 2
public void read() {
   List list = map.get(1L);
}

调用 map.get(1L) return ArrayList 的状态是否正确(例如大小是否正确)?

ArrayList 的元素呢?元素会安全发布吗?

我注意到 ConcurrentHashMap.put 由 Node 上的锁保护,将被修改,但 ConcurrentHashMap.get 执行到 Unsafe.getObjectVolatile

而且我认为 ArrayList 可能发布不安全

谢谢!

ConcurrentHashMap 是线程安全的,但 ArrayList 不是

意味着如果 reader 在作者的 put 调用之前调用 get,则结果将为 null。如果作者在 reader 的 get 调用之前调用 put,则结果将是 List object

因此,如果您将项目添加到列表中(代码如下)

for (int i = 0; i <100 ; i++) {
   list.add(new SomeStuff(i))
}

put 之前就可以了,因为无法从 reader

访问该列表

但如果在 put 之后,它将可以从 reader 访问并导致 reader 和写入器同时读取或写入 ArrayList。这可能会导致问题,因为 ArrayList 不是线程安全的。

简短的回答:是的,ArrayList 对象将与其元素一起安全发布。

您通常不应查看 class 的源代码并自行得出任何关于线程安全的结论,而应该依赖 class/interface javadoc 中提供的保证。我强烈推荐详述此类方法的书 "Java Concurrency in Practice"。

ConcurrentMap(由ConcurrentHashMap实现)接口声明:

Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a ConcurrentMap as a key or value happen-before actions subsequent to the access or removal of that object from the ConcurrentMap in another thread.

这意味着在 "put" 之前发生的任何事情对于之后在同一对象上创建 "get" 的线程都是可见的。这意味着如果 "get" 发生在 "put" 之后,那么您的第二个线程将看到一个 "good" ArrayList 对象及其所有元素。

这里有一条与安全发布无关的注意事项,只是需要记住的事项:

ArrayList 本身(可能还有您的元素的 class)不是线程安全的。但是只要您不从其他线程修改这些对象,您就可以接受它。如 "Java Concurrency in Practice" 书中所述 - 这是 "Serial Thread Confinement" 这意味着您可以安全地将非线程安全对象从一个线程发布到另一个线程,从而将 "ownership" 从一个线程传输到另一个线程线程,从而使对象在特定时间点 "Thread Confined"。正如我所说,只有当您确保它没有被其他地方修改时它才适用。