当我在课堂上使用场注入时,如下所示:
@Inject
private MyClass myField;
我可以对此字段的“safe publication”状态做出任何假设吗?或者换一种说法,假设MyClass本身是线程安全的,那么在使用这个字段时我是否应该注意任何并发风险?
我的直觉通常是尽可能创建所有字段final,但这不适用于字段注入。当然,我可以使用构造函数注入,但是我通常最终只需要为代理创建一个额外的“假”no-args构造函数。没有太大问题,但使用现场注入更方便。另一种选择可能是将该字段标记为易变(或甚至使用锁定......),但这是否真的有必要?
JSR-299规范似乎没有回答这个问题。我在像Weld这样的实现上使用CDI。
答案 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
,那么同一个实例(或其代理)可以在同一个上下文中注入多个注入点。例如,如果来自同一会话的并行浏览器请求访问MyClass
且MyClass
已注释@SessionScoped
,则可以让多个线程并行访问同一实例。 CDI不会为您提供任何同步,因此您必须确保MyClass
是线程安全的。