Are injected (@Inject) fields safely published?
Asked Answered
S

4

9

When I use field injection in a class, like so:

@Inject
private MyClass myField;

can I make any assumption about the "safe publication" status of this field? Or put differently, and assuming that MyClass itself is thread-safe, are there any concurrency risks I should be aware of when using this field?

My instinct is usually to create all fields final if possible, but that doesn't work with field injection. Of course, I can use constructor injection, but then I usually end up having to create an additional "fake" no-args constructor just for proxying. Not much of a problem, but using field injection is just more convenient. Another option could be marking the field as volatile (or even using a lock on it...), but is that really necessary?

The JSR-299 spec does not seem to answer this question. I'm using CDI on implementations like Weld.

  • The object into which I'm injecting will be used by multiple threads (it's @ApplicationScoped, for instance). I want this.
  • I understand that if MyClass is immutable, safe publication is not a concern. But I don't necessarily inject only immutable objects.
  • MyClass itself is assumed to be thread safe; this is not my concern. The concern is strictly about unsafe publication, e.g. the possibility of threads seeing half-constructed instances of MyClass, due to the rules of the Java Memory Model.
Sumerology answered 13/12, 2012 at 16:43 Comment(1)
Yes, using CDI sometimes necessitates writing the code a little less defensive when using access modifiers and final. But I see no reasonable doubt that thread-safety would be an oversight in CDI-spec. So while I can't explain this I for myself feel pretty save. One of our applications also have 60.000 users and it's working out fineBreakage
M
3

Perhaps the thread safety for this situation has been deliberately left out from the specification, which means thread safety is not guaranteed.

Let's think: if a field written by one thread is read by some other thread, unless there is some form of happens-before relation, the other thread may read stale data. Guice ultimately uses either reflection to set the value of myField, or it may use an auto-generated setter. There is no happens-before relation so that reflection-write happens-before field-read or method-invocation happens-before field-read (unless locks, volatiles or other means are used which forms the happens-before relation).

Therefore, I would say that there is a (perhaps rather low) possibility of seeing null values.

EDIT: as per http://bit.ly/1m4AUIz writing to final field after the constructor ends (via reflection) holds the same semantics as initializing the field in the constructor. So, make Guice-injected fields final, set them to null and it should work correctly. This is indeed a very dark JVM corner :-) Moreover, as per http://bit.ly/1m4AwJU Guice performs injections in exactly one thread which makes it thread-safe... Seems weird to me performance-wise, but apparently it works this way.

Mesics answered 28/6, 2013 at 13:39 Comment(1)
Good to know, but Guice isn't a CDI implementation, and as explained under Rob's answer, CDI rules out injection of final fields.Sumerology
S
2

I believe you can based on section 9.1.1 of the java memory model: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf 9.1.1 Post-Construction Modification of Final Fields ... Freezes of a final field occur both at the end of the constructor in which the final field is set, and immediately after each modification of a final field via reflection other special mechanism. ...

Some related Guice discussions are: http://markmail.org/message/fxs5k32dihpoy5ry#query:bob%20lee%20constructor%20injection+page:1+mid:fxs5k32dihpoy5ry+state:results

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

It would be nice if DI frameworks made this explicit statement though.

Saphena answered 3/7, 2013 at 4:30 Comment(1)
While it seems that Guice allows to use field injection on final fields, the CDI spec specifically excludes that: "An injected field is a non-static, non-final field of a bean class ..." (3.8). Also, the CDI spec doesn't seem to say anything about whether injection is done in a single thread or not.Sumerology
A
1

I always use constructor injection. Then your fields may be final and there is no question about their thread safety.

Alded answered 14/12, 2012 at 8:8 Comment(5)
So do I, but more out of a gut feeling than solid knowledge. And this approach forces you to provide an awkward "dummy" no-args constructor that simply nulls all the fields, right? Otherwise the class wouldn't be proxyable.Sumerology
You can annotated a constructor with @Inject and it will inject al of the args into the constructor, then you can proceed as you normally do. Of course any qualifiers will need to be on the constructor args.Highkey
I know how constructor injection works, as stated in the original question. But when I add a constructor with arguments (injection-annotated or not), I also need to add an explicit no-args constructor as well to keep the class proxyable. And in the case of final fields, I need to set all the fields to something (null in this case). That's a bit awkward.Sumerology
Can't say I know enough about CDI / Weld proxying to help with that. That said, I usually try to only proxy interfaces (not concrete classes) and then there is no need for dummy constructors.Alded
Good point about using interfaces. However, sometimes they're not useful and simply add boilerplate.Sumerology
A
1

Any concurrency risks when using an injected instance depend on the effective scope of that instance.

If MyClass is in the default @Dependent scope, each injection point will get its own instance. The precautions you take with respect to thread safety would be the same as if you called new MyClass() yourself. If you access that instance from multiple threads, you'll need to make sure that MyClass is thread-safe or provide some synchronization around it.

If MyClass is in a wider scope such as @SessionScoped or @ApplicaionScoped, then the same instance (or a proxy to it) could be injected into multiple injection points in the same context. For example, If you have parallel browser requests from the same session accessing MyClass and MyClass is annotated @SessionScoped, you could have multiple threads accessing the same instance in parallel. CDI isn't going to put any synchronization around this for you, so you'd have to make sure that MyClass is thread safe.

Atwell answered 14/12, 2012 at 18:19 Comment(1)
My question is not about whether an injected instance may be exposed to access by multiple threads or not - I know that, and actually want it. One typical use case is a JAX-RS resource class that's ApplicationScoped and gets some fields injected. Of course this means that many threads will acccess the same instance. MyClass itself may be perfectly thread-safe (e.g. by having all field accesses guarded by a lock) and could still be published in an unsafe manner (see this). Does CDI do anything about this?Sumerology

© 2022 - 2024 — McMap. All rights reserved.