When doing lazy initialization of a static singleton in Java you can do this:
public class Bob {
private static class SingletonWrapper {
private static final Bob instance = new Bob();
}
public static Bob getInstance() {
return SingletonWrapper.instance;
}
}
Because the inner class SingletonWrapper
is only loaded when first accessed the Bob()
is not created until getInstance()
is called.
My question is whether there are any similar tricks that can be used to do a lazy instantiation of a member variable in a non-static context.
public class Bob {
// Clearly this doesn't work as not lazy
private final InnerWrapper wrapper = new InnerWrapper();
private class InnerWrapper {
private final Jane jane = new Jane();
}
public Jane getJane() {
return wrapper.jane;
}
}
Is there any way we can have an instance of Jane
within Bob
and thread safely have the instance only created on demand without using double check locking or AtomicReference
. Ideally the get method should remain as simple as the one in these examples but if that is not possible then the simplest and fastest possible (most efficient) execution of the get
method would be ideal.
No, there are no synchronization rules for instantiating types like there are for initializing classes. You have to add them yourself. Whether you do it with double checked locking or some other mechanism is up to you.
Beginning with Java 8, I like to use ConcurrentHashMap#computeIfAbsent
to achieve laziness.
class Bob {
private final ConcurrentHashMap<String, Jane> instance = new ConcurrentHashMap<>(1);
public Jane getJane() {
return instance.computeIfAbsent("KEY", k -> new Jane()); // use whatever constant key
}
}
There are also these solutions for lazy initializing without the thread-safety constraint. I was not able to adapt them cleanly for a multithreaded context.
As stated in the comments, double checked locking will always be faster than these solutions as they don't include all the fluff for hiding the implementation.
You can use Guava's cache:
public class Bob {
private final static Object KEY = new Object();
private final Cache<Object, Jane> cache =
CacheBuilder.newBuilder()
.build(new CacheLoader<Object, Jane>() {
@Override
public Jane load() {
return new Jane();
}
});
public Jane getJane() {
return cache.get(KEY);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With