What is the best way to write singleton?
Answer is Enums. This
approach is functionally equivalent to the public field approach, except that
it is more concise, provides the serialization machinery for free, and provides
an ironclad guarantee against multiple instantiation, even in the face of
sophisticated serialization or reflection attacks. While this approach has yet
to be widely adopted, a single-element enum type is the best way to implement a
singleton.
Before we come to enum
lets see what there before Pre Java5. How people use to write singleton
classes.
While implementing
Singleton we have 2 options
1. Lazy loading
2. Early loading
While implementing we
encounter some problems like multi thread safe, duplication through
serialization
Lazy Loading
public class LazyLoadingSingleton
{
// Our now_null_but_going_to_be sole hero
private static LazyLoadingSingleton
INSTANCE = null;
private LazyLoadingSingleton() {
if (INSTANCE != null) {
// SHOUT
throw new
IllegalStateException("Already instantiated");
}
}
public static LazyLoadingSingleton
getInstance() {
// Creating only when required.
if (INSTANCE == null) {
INSTANCE = new LazyLoadingSingleton();
}
return INSTANCE;
}
}
Early loading
public class
EarlyLoadingSingleton {
// It will be our sole hero
private static final
EarlyLoadingSingleton INSTANCE = new EarlyLoadingSingleton();
private EarlyLoadingSingleton() {
if (INSTANCE != null) {
// SHOUT
throw new
IllegalStateException("Already instantiated");
}
}
public static EarlyLoadingSingleton
getInstance() {
return INSTANCE;
}
}
But both example are not safe in perspective of
multi threaded environment.
public class
DoubleCheckedLockingSingleton {
// Pay attention to volatile
private static volatile
DoubleCheckedLockingSingleton INSTANCE = null;
private DoubleCheckedLockingSingleton() {
if (INSTANCE != null) {
// SHOUT
throw new
IllegalStateException("Already instantiated");
}
}
public static
DoubleCheckedLockingSingleton getInstance() {
if (INSTANCE == null) { // Check 1
synchronized
(DoubleCheckedLockingSingleton.class) {
if (INSTANCE == null) { // Check 2
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
Now we are sure about multi thread but what about the
serialization? We have to make sure even while deserialiation no new object is
created.
public class
SerializationSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static volatile
SerializationSingleton INSTANCE = null;
private SerializationSingleton() {
if (INSTANCE != null) {
// SHOUT
throw new
IllegalStateException("Already instantiated");
}
}
// double check for synchronization
public static
SerializationSingleton getInstance() {
if (INSTANCE == null) { // Check 1
synchronized
(SerializationSingleton.class) {
if (INSTANCE == null) { // Check 2
INSTANCE = new SerializationSingleton();
}
}
}
return INSTANCE;
}
// No more fear of
serialization
@SuppressWarnings("unused")
private SerializationSingleton readResolve()
{
return INSTANCE;
}
}
The method readResolve() will make sure the only instance
will be returned, even when the object was serialized in a previous run of our
program.
Finally we have added enough
protection against threads and serialization but our code is looking bulky and
ugly. Let’s simplify and write everything in one class.
public final class
MultiThreadSyncSerializationSafe implements Serializable {
private static final long serialVersionUID = 1L;
// Wrapped in a inner static class so that
loaded only when required
private static class FooLoader {
// And no more fear of threads
private static final MultiThreadSyncSerializationSafe INSTANCE = new
MultiThreadSyncSerializationSafe();
}
private MultiThreadSyncSerializationSafe() {
if (FooLoader.INSTANCE != null) {
// SHOUT
throw new
IllegalStateException("Already instantiated");
}
}
public static MultiThreadSyncSerializationSafe
getInstance() {
return FooLoader.INSTANCE;
}
// No more fear of serialization
@SuppressWarnings("unused")
private MultiThreadSyncSerializationSafe
readResolve() {
return FooLoader.INSTANCE;
}
}
Here is the best way to achieve everything we did above is
best possible way.
public enum Foo {
INSTANCE;
}
That's it no more fear of serialization, threads and ugly
code. ENUMS singleton
are lazily initialized.
Ok still wondering? Here is more to understand.
public enum EnumSingleton {
INSTANCE;
private final String[] favoriteSongs = { "All
Rise",
"Hundreds
days"
};
public void printFavorites() {
System.out.println(Arrays.toString(favoriteSongs));
}
public static void main(String[] args)
{
EnumSingleton.INSTANCE.printFavorites();
}
}
No comments:
Post a Comment