...
Code Block | ||
---|---|---|
| ||
class Foo { // If perThreadInstance.get() returns a non-null value, this thread // has done synchronization needed to see initialization of helper private final ThreadLocal perThreadInstance = new ThreadLocal(); private Helper helper = null; public Helper getHelper() { if (perThreadInstance.get() == null) { createHelper(); } return helper; } private final void createHelper() { synchronized(this) { if (helper == null) { helper = new Helper(); } // Any non-null value would do as the argument here perThreadInstance.set(perThreadInstance); } } } |
Compliant Solution (java.util.concurrent
utilities)
This compliant solution uses an AtomicReference
wrapper around the Helper
object. It uses the standard compareAndSet
functionality to set a newly created Helper
object if helperRef
is null
. Otherwise, it simply returns the already created instance. (Tom Hawtin, JMM Mailing List)
Code Block | ||
---|---|---|
| ||
// Uses atomic utilities
class Foo {
private final AtomicReference<Helper> helperRef =
new AtomicReference<Helper>();
public Helper getHelper() {
Helper helper = helperRef.get();
if (helper != null) {
return helper;
}
Helper newHelper = new Helper();
return helperRef.compareAndSet(null, newHelper) ?
newHelper :
helperRef.get();
}
}
|
Compliant Solution (immutable
)
In this solution the Foo
class is unchanged (it is immutable as before), but the Helper
class is immutable. In this case, the Helper
class is guaranteed to be fully constructed before becoming visible. The object must be truly immutable; it is not sufficient for the program to refrain from modifying the object.
...