In order to use an effectively immutable object without final
fields in a thread safe manner you need to use one of safe publication idioms when making the object available to other threads after initialization, otherwise these threads can see the object in partially initialized state (from Java Concurrency in Practice):
- Initializing an object reference from a static initializer;
- Storing a reference to it into a volatile field or AtomicReference;
- Storing a reference to it into a final field of a properly constructed object; or
- Storing a reference to it into a field that is properly guarded by a lock.
Declaring fields of your immutable object as final
releases this restriction (i.e. it guarantees that if other threads see a reference to the object, they also see its final
fields in fully initialized state). However, in general case it doesn't guarantee that other threads can see a reference to the object as soon as it was published, so you may still need to use safe publication to ensure it.
Note that if your object implements an interface, you can use an approach used by Collections.unmodifiableList()
, etc:
class ImmutableFooWrapper implements IFoo {
private final IFoo delegate; // final provides safe publication automatically
public ImmutableFooWrapper(IFoo delegate) {
this.delegate = delegate;
}
...
}
public IFoo immutableFoo(IFoo foo) {
return new ImmutableFooWrapper(foo);
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…