LazyReference with double-checked locking and null handling
I've been using LazyReference
class for a few years (not on a regular basis of course, but sometimes it is very useful). The class can be seen here. Credits go to Robbie Vanbrabant (class author) and Joshua Bloch with his famous "Effective Java 2nd edt." (original code).
The class works correctly (in Java 5+) but there is one little potential issue. If instanceProvider
returns null
(well it must not according to Guice Provider.get()
contract, but…) then on every execution of LazyReference.get()
method the LOCK will be held and instanceProvider.get
will be called over and over again. It looks like a good punishment for those who break the contracts (he-he), but what if one really needs to lazily initialize a field with the possibility to set the开发者_开发技巧 null
value?
I modified LazyReference a little bit:
public class LazyReference<T> {
private final Object LOCK = new Object();
private volatile T instance;
private volatile boolean isNull;
private final Provider<T> instanceProvider;
private LazyReference(Provider<T> instanceProvider) {
this.instanceProvider = instanceProvider;
}
public T get() {
T result = instance;
if (result == null && !isNull) {
synchronized (LOCK) {
result = instance;
if (result == null && !isNull) {
instance = result = instanceProvider.get();
isNull = (result == null);
}
}
}
return result;
}
}
IMHO it should work just fine (if you have another opinion please post your comments and criticisms). But I wonder what will happen if I remove the volatile
modifier from isNull
boolean (leaving it for instance
of course)? Will it still work correctly?
The above code has a race condition: instance may be set to the "real" null from the result of instanceProvider.get() before isNull has been set.
Are you sure it wouldn't be easier for you to just scrap this complicated nonsense and synchronise properly. I bet you will not be able to measure any difference in performance and it will be easier to verify that your code is correct.
As pointed out by Neil Coffey, this code contains a race condition, but it can be easily fixed as follows (note that instance
don't need to be volatile
):
public class LazyReference<T> {
private T instance;
private volatile boolean initialized;
...
public T get() {
if (!initialized) {
synchronized (LOCK) {
if (!initialized) {
instance = instanceProvider.get();
initialized = true;
}
}
}
return instance;
}
}
精彩评论