Re: SingletonHolder reinvented.
On Feb 12, 7:55 am, "Andrey Ryabov" <andrey_rya...@bk.ru> wrote:
Just simplified version:
public class SingletonHolder<T> implements Callable<T> {
private AtomicReference<T> _instance = new AtomicReference<T>();
private AtomicReference<FutureTask<T>> _future = new
AtomicReference<FutureTask<T>>();
public T get() {
try {
T result = _instance.get();
if (result != null) {
return result; // Return value if it has already been initialized.
}
if (_future.compareAndSet(null, new FutureTask<T>(this))) { //
create and try to set to _future
_future.get().run(); // run the task if previous operation
succeed, executed only once!
}
result = _future.get().get(); // get result of execution..
if (result == null) {
throw new IllegalStateException(...); // It must not be null
}
_instance.compareAndSet(null, result); // set only if it was not
set already
return result;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(...);
} catch (ExecutionException e) {
throw new RuntimeException(...);
}
}
// This method is to be overridden by subclasses.
public T call() throws Exception {
throw new IllegalStateException("call is not implemented");
}
}
What's wrong with this approach:
import java.util.concurrent.Future;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class SafeSingletonFactory<T> {
private T instance;
final Future<T> factory;
public SafeSingletonFactory(Callable<T> factory) {
this.factory = new FutureTask<T>(factory);
}
public T get() throws InterruptedException {
try {
return instance == null ? instance = factory.get() :
instance;
} catch (InterruptedException e) {
throw e;
} catch (ExecutionException e) {
if (e.getCause() instanceof Error) {
throw (Error)e.getCause();
}
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException)e.getCause();
}
throw new RuntimeException("Exception while initializing
singleton.", e.getCause());
}
}
}
This way, you don't have to worry about:
1. Thread safety and synchronization (delegated to FutureTask.get())
2. Extending your singleton class. One class can have many
SafeSingletonFactory<T> instances.
3. atomic operations (again, delgated to FutureTask.get());
So, to use in a Static Singleton Factory Method (which IMHO is a bad
idea to use, but seems to be the common idiom used for singletons:
public class MySingleton {
public final static SafeSingletonFactory<MySingleton> instance =
new SafeSingletonFactory<MySingleton>(new
Callable<MySingleton>() {
public MySingleton call() {
return new MySingleton();
}
}
private MySingleton() {
}
}
Thus, you can call MySingleton.instance.get() safely from anywhere,
without too much overhead.
Actually, you could probably just use a Future<MySingleton> instance;
but it can be prematurely cancled by client code, and client code has
more exception handling to do.
I have to say though, in my experience, most Singleton patterns can be
refactored into Dependency Injection with a lot of benefit to the
overal program design.