ExecutorService(特别是ThreadPoolExecutor)线程安全吗?

2022-08-31 12:02:21

是否保证螺纹安全?ExecutorService

我将把来自不同线程的作业提交到同一个 ThreadPoolExecutor,我是否必须在交互/提交任务之前同步对执行器的访问?


答案 1

(与其他答案相反)线程安全契约记录下来:查看javadocs(而不是方法的javadoc)。例如,在 ExecutorService javadoc 的底部,您可以找到:interface

内存一致性影响:在将 Runnable 或 Callable 任务提交给 ExecutorService 之前,线程中的操作发生在该任务执行器服务执行的任何操作之前,而这些操作又发生在通过 Future.get() 检索结果之前

这足以回答这个问题:

“在交互/提交任务之前,我是否必须同步对执行程序的访问?”

不,你没有。在没有外部同步的情况下构造作业并将其提交到任何(正确实现)都可以。这是主要设计目标之一。ExecutorService

ExecutorService是一个并发实用程序,也就是说,为了提高性能,它被设计为在不需要同步的情况下最大程度地运行。(同步会导致线程争用,这可能会降低多线程效率 - 尤其是在扩展到大量线程时。

不能保证任务将在将来的什么时间执行或完成(有些甚至可能在提交任务的同一线程上立即执行),但是工作线程可以保证已经看到了提交线程在提交之前执行的所有效果。因此(运行的线程),您的任务还可以安全地读取为其使用而创建的任何数据,而无需同步,线程安全类或任何其他形式的“安全发布”。提交任务的行为本身就足以将输入数据“安全发布”到任务。您只需要确保在任务运行时不会以任何方式修改输入数据。

类似地,当您通过 获取任务结果时,检索线程将保证看到执行器的工作线程产生的所有效果(在返回的结果中,加上工作线程可能所做的任何副作用更改)。Future.get()

此合约还意味着任务本身可以提交更多任务。

“执行器服务是否保证线程安全?”

现在,问题的这一部分要普遍得多。例如,找不到任何关于该方法的线程安全协定的语句 - 尽管我注意到Javadoc中的代码示例不使用同步。(尽管可能有一个隐藏的假设,即关闭是由创建执行程序的同一线程引起的,而不是例如工作线程?shutdownAndAwaitTermination

顺便说一句,我会推荐“Java并发实践”一书,因为它是并发编程世界的良好基础。


答案 2

的确,有问题的JDK类似乎没有明确保证线程安全的任务提交。但是,在实践中,库中的所有 ExecutorService 实现确实以这种方式实现线程安全。我认为依靠这一点是合理的。由于所有实现这些功能的代码都放在公共领域,因此绝对没有人有动力以不同的方式完全重写它。