使用SinglethreadExecutor时需要线程安全

时间:2012-01-03 08:04:36

标签: java thread-safety

我正在开发一个Java EE应用程序,该应用程序从UI接收用户请求,然后使用ExecutorService(SinglethreadExecutor)为每个请求异步键入lon工作流。现在因为我使用的是SinglethreadExecutor,并且因为确实需要一次一个地提供请求,所以我觉得不需要线程安全。 我的理解是否正确?

最近我问了一个问题Issue when executing asynchronous tasks using ExecutorService,这个问题的解决方案就是让我的代码线程安全。 我正在寻找我在我的代码中使用的任何共享资源是否导致需要这个线程安全,但我只想确定我的理解 情景是正确的。

仅供参考,我在Running a background Java program in Tomcat中提到的servlet中实现了我的ExecutorService

2 个答案:

答案 0 :(得分:3)

您的请求将被传递到另一个要执行的线程。即使此线程不访问共享数据结构,也需要正确地同步将请求传递给线程并返回结果。

如果您使用submitinvoke方法之一使用Future对象返回结果,则可以假定执行了适当的同步。 ExecutorService的javadoc说明了这一点:

  

内存一致性效果:在向ExecutorService提交Runnable或Callable任务之前的一个线程中的操作发生在该任务执行的任何操作之前,而该操作又发生在通过Future.get()检索结果之前

简而言之,如果请求/任务不使用共享数据结构并且您使用提供的接口方法,那么您应该没问题。

答案 1 :(得分:1)

当您需要创建“线程安全”时,需要考虑两件事:线程什么时候让其他人看到数据?线程何时尝试读取共享数据?

想象一下这种情况:线程A获取请求。它有点工作。然后它调用方法foo(),将请求作为参数获取。 foo()开始一个新线程。该线程将对请求的引用作为私有的非final字段。

在硬件中,线程A已将请求复制到运行它的CPU核心的L1缓存中。由于两个线程之间没有同步,因此A不知道某个其他线程可能想要读取修改后的请求,因此它永远不会刷新缓存(或者它太迟了)。

这意味着线程B将获得陈旧的请求对象。它不会看到线程A所做的任何更改。您可以想象,这通常有效:如果A不更改请求,则B工作。一旦你更改了A的代码就会中断,你有一个“但它昨天工作了!”情况。

要解决此问题,您必须告诉A刷新其缓存,即使当前版本的代码没有它也能正常工作。有几种方法可以做到;斯蒂芬C描述了一个。另外两种方式:

  • 您可以synchronize foo() - 线程在进入同步块时必须刷新。
  • 使请求成为B的final字段 - 必须在类型构造完成时完全刷新通过final字段引用的对象图形(其中type ==包含最终字段的类)。