Java集合线程安全地将集合对象从一个线程传递到另一个线程

Ale*_*eld 11 java thread-safety

我有一个Java应用程序,它有工作线程来处理作业.一个worker产生一个结果对象,比如说:

class WorkerResult{
    private final Set<ResultItems> items;
    public Worker(Set<ResultItems> pItems){
         items = pItems;
    }
}
Run Code Online (Sandbox Code Playgroud)

当工人完成时,它执行此操作:

 ...
 final Set<ResultItems> items = new SomeNonThreadSafeSetImplSet<ResultItems>();
 for(Item producedItem : ...){
      items.add(item);
 }
 passToGatherThread(items);
Run Code Online (Sandbox Code Playgroud)

这套items装置在这里是一种"工作单元".该passToGatherThread方法将items集合传递给一个收集线程,其中只有一个在运行时存在.

这里不需要同步,因为竞争条件不会发生,因为只有一个线程(Gather-thread)读取该items集合.AFAICS,Gather-thread可能看不到所有项目,因为该集合不是线程安全的,对吧?

假设我无法进行passToGatherThread同步,因为它是第三方库.我基本上担心的是收集线程由于缓存,VM优化等而没有看到所有项目.所以这里出现了一个问题:如何以线程安全的方式传递项目集,以便Gather线程"看到"适当的项目?

Dar*_*usz 1

我对这个问题思考(并讨论)了很多,并且提出了另一个答案,我希望这将是最好的解决方案。

传递同步集合在效率方面并不好,因为该集合上的每个后续操作都将被同步 - 如果有很多操作,则可能会成为一个障碍。

重点是:让我们做一些假设(我不同意):

  • 上述passToGatherThread方法确实不安全,但看起来不太可能
  • 编译器可以对代码中的事件重新排序,以便passToGatherThread在填充集合之前调用

确保传递给 Gatherer 方法的集合准备就绪且完整的最简单、最干净且可能最有效的方法是将集合推送放入同步块中,如下所示:

synchronized(items) {
  passToGatherThread(items);
}
Run Code Online (Sandbox Code Playgroud)

这样我们就可以在传递集合之前确保内存同步和有效的发生前序列,从而确保所有对象都正确传递。