9

当我在类中使用字段注入时,如下所示:

@Inject
private MyClass myField;

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

我的直觉通常是尽可能创建所有字段 final ,但这不适用于字段注入。当然,我可以使用构造函数注入,但是我通常最终不得不创建一个额外的“假”无参数构造函数来进行代理。问题不大,但使用字段注入更方便。另一种选择可能是将字段标记为易失性(甚至使用锁定...),但这真的有必要吗?

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

  • 我注入的对象被多个线程使用(例如,@ApplicationScoped)。我要这个。
  • 我知道如果 MyClass 是不可变的,安全发布就不是问题。但我不一定只注入不可变对象。
  • MyClass 本身被假定为线程安全的;这不是我关心的问题。由于 Java 内存模型的规则,严格来说,关注的是不安全的发布,例如线程看到 MyClass 的半构造实例的可能性。
4

4 回答 4

3

也许这种情况下的线程安全性被故意从规范中遗漏了,这意味着线程安全性没有得到保证。

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

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

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

于 2013-06-28T13:39:24.823 回答
2

我相信您可以根据 java 内存模型的第 9.1.1 节: http ://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf 9.1.1 最终字段的构造后修改.. . final 字段的冻结发生在设置了 final 字段的构造函数的末尾,以及通过反射其他特殊机制每次修改 final 字段之后。...

一些相关的 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 框架做出这样的明确声明,那就太好了。

于 2013-07-03T04:30:12.597 回答
1

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

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

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

于 2012-12-14T18:19:52.773 回答
1

我总是使用构造函数注入。那么您的字段可能是最终的,并且它们的线程安全性毫无疑问。

于 2012-12-14T08:08:39.870 回答