注入(@Inject)字段是否安全发布?

时间:2012-12-13 16:43:04

标签: java thread-safety cdi weld

当我在课堂上使用场注入时,如下所示:

@Inject
private MyClass myField;

我可以对此字段的“safe publication”状态做出任何假设吗?或者换一种说法,假设MyClass本身是线程安全的,那么在使用这个字段时我是否应该注意任何并发风险?

我的直觉通常是尽可能创建所有字段final,但这不适用于字段注入。当然,我可以使用构造函数注入,但是我通常最终只需要为代理创建一个额外的“假”no-args构造函数。没有太大问题,但使用现场注入更方便。另一种选择可能是将该字段标记为易变(或甚至使用锁定......),但这是否真的有必要?

JSR-299规范似乎没有回答这个问题。我在像Weld这样的实现上使用CDI。

  • 我注入的对象将被多个线程使用(例如,@ ApplicationScoped)。我想要这个。
  • 我明白如果MyClass是不可变的,安全发布不是问题。但我不一定只注入不可变对象。
  • 假设MyClass本身是线程安全的;这是我的担忧。关注的是严格关于不安全的出版物,例如:由于Java内存模型的规则,线程可能会看到半构造的MyClass实例。

4 个答案:

答案 0 :(得分:3)

这种情况的线程安全可能是故意忽略的,这意味着线程安全性无法保证。

让我们想一想:如果一个线程写入的字段被某个其他线程读取,除非存在某种形式的先发生关系,否则另一个线程可能会读取陈旧数据。 Guice最终使用反射来设置myField的值,或者它可以使用自动生成的setter。没有发生之前的关系,以便在字段读取或方法调用发生之前发生反射 - 写入 - 在字段读取之前(除非使用锁定,挥发性或其他方式形成事先关系)。

因此,我会说看到空值的可能性很小(可能相当低)。

编辑:按照http://bit.ly/1m4AUIz在构造函数结束后(通过反射)写入final字段时保持与在构造函数中初始化字段相同的语义。因此,将Guice注入的字段设为final,将它们设置为null,它应该正常工作。这确实是一个非常黑暗的JVM角落:-) 而且,根据http://bit.ly/1m4AwJU,Guice只在一个线程中执行注入,这使得它在线程安全......对我而言,在性能方面似乎很奇怪,但显然它是这样工作的。

答案 1 :(得分:2)

我相信你可以基于java内存模型的第9.1.1节:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf 9.1.1最终字段的构造后修改... 冻结最终字段在最终字段设置的构造函数的末尾,并且在通过反射每次修改最终字段后立即其他特殊机制。 ...

一些相关的Guice讨论是: http://markmail.org/message/fxs5k32dihpoy5ry#query:bob%20lee%20constructor%20injection+page:1+mid:fxs5k32dihpoy5ry+state:results

..和http://www.theserverside.com/discussions/thread.tss?thread_id=52252#284713

如果DI框架制作了这个明确的陈述,那将是很好的。

答案 2 :(得分:1)

我总是使用构造函数注入。然后你的领域可能是最终的,毫无疑问他们的线程安全。

答案 3 :(得分:1)

使用注入实例时的任何并发风险取决于该实例的有效范围。

如果MyClass位于默认的@Dependent范围内,则每个注入点都将获得自己的实例。您对线程安全采取的预防措施与您自己致电new MyClass()时的预防措施相同。如果从多个线程访问该实例,则需要确保MyClass是线程安全的或围绕它提供一些同步。

如果MyClass的范围更广,例如@SessionScoped@ApplicaionScoped,那么同一个实例(或其代理)可以在同一个上下文中注入多个注入点。例如,如果来自同一会话的并行浏览器请求访问MyClassMyClass已注释@SessionScoped,则可以让多个线程并行访问同一实例。 CDI不会为您提供任何同步,因此您必须确保MyClass是线程安全的。