Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are injected (@Inject) fields safely published?

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.
like image 409
HansMari Avatar asked Dec 13 '12 16:12

HansMari


People also ask

Why field based injection is not recommended?

The reasons why field injection is frowned upon are as follows: You cannot create immutable objects, as you can with constructor injection. Your classes have tight coupling with your DI container and cannot be used outside of it. Your classes cannot be instantiated (for example in unit tests) without reflection.

Why Autowired is not recommended?

If we had used Autowired, we would have been getting the following error in the test. Therefore, it is not recommended to use Autowired. Safety — Forces Spring to provide mandatory dependencies. We make sure that the created objects are valid after construction.

How does an injected annotation work?

Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments. @Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.

Why constructor injection is better than field injection?

At this related question there are some valid arguments why to use injection via constructor instead of injection via field. It boils down to the advantage that you can use initialization via constructor also in non-CDI environment i.e. Unit Test, without the need to add more complex logic.


4 Answers

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.

like image 190
Martin Vysny Avatar answered Oct 22 '22 00:10

Martin Vysny


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.

like image 42
Rob Bygrave Avatar answered Oct 22 '22 00:10

Rob Bygrave


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

like image 20
Steven Schlansker Avatar answered Oct 22 '22 00:10

Steven Schlansker


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.

like image 38
Brian Avatar answered Oct 22 '22 01:10

Brian