best practice: Sinigleton w/ enum
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
pro:
concise, and thread-safe.
enum is singleton by design. All the enum values are initialized only once when class loaded.
enum 支持反序列化机制,无需实现 Serializable 接口和重写 readResolve() 方法,即可避免反序列化中对单例模式的破坏。
enum can’t be initialized via reflection. 试图通过反射方式得到单例对象,会报错:java.lang.IllegalArgumentException: Cannot reflectively create enum objects.
con: none.
Traditional Methods of Making Singletons:
2.1 : Eagerly Initialized Singleton
public class EagerSingleton {
/** private constructor to prevent others from instantiating this class */
private EagerSingleton() {}
/** Create an instance of the class at the time of class loading */
private static final EagerSingleton instance = new EagerSingleton();
/** Provide a global point of access to the instance */
public static EagerSingleton getInstance() {
return instance;
}
}
pro: thread-safe.
con: the instance is created irrespective of whether it is accessed or not. This is fine if the object is simple and does not hold any system resources. But can have performance implications if it allocates a large amount of system resources and remains unused.
2.2 : Eagerly Initialized Static Block Singleton
public class EagerStaticBlockSingleton {
private static final EagerStaticBlockSingleton instance;
/** Don't let anyone else instantiate this class */
private EagerStaticBlockSingleton() {}
/** Create the one-and-only instance in a static block */
static {
instance = new EagerStaticBlockSingleton();
}
/** Provide a public method to get the instance that we created */
public static EagerStaticBlockSingleton getInstance() {
return instance;
}
}
pro:thread-safe.
con:the instance is created whether or not it is needed by the application.
2.3 : Lazily Initialized Singleton
public class LazySingleton {
private static LazySingleton instance;
/** Don't let anyone else instantiate this class */
private LazySingleton() {}
/** Lazily create the instance when it is accessed for the first time */
public static synchronized LazySingleton getInstance() {
if(instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
pro:The synchronized keyword ensures thread-safety.
con:The synchronized solution is kinda inefficient.
2.4 : Lazily Initialized Double-Checked Locking Singleton
public class LazyDoubleCheckedLockingSingleton {
private static volatile LazyDoubleCheckedLockingSingleton instance;
/** private constructor to prevent others from instantiating this class */
private LazyDoubleCheckedLockingSingleton() {}
/** Lazily initialize the singleton in a synchronized block */
public static LazyDoubleCheckedLockingSingleton getInstance() {
if(instance == null) {
synchronized (LazyDoubleCheckedLockingSingleton.class) {
// double-check
if(instance == null) {
instance = new LazyDoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
pro:the use of volatile keyword is necessary here to prevent compilers from doing their own optimizations (like instruction reordering). and it's thread-safe.
con:not very efficient.
2.5 Lazily Initialized Inner Class Singleton
public class LazyInnerClassSingleton {
/** private constructor to prevent others from instantiating this class */
private LazyInnerClassSingleton() {}
/** This inner class is loaded only after getInstance() is called for the first time. */
private static class SingletonHelper {
private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
}
public static LazyInnerClassSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
pro: most efficient due to the reason that the inner class is not loaded until the getInstance() method is invoked for the first time. This solution is thread-safe and doesn’t require any other synchronization.
con: none.
Conclusion:
The examples above are several thread-safe ways of implementing the singleton design pattern. (who would want thread-unsafe solutions?)
Last but not the least, 简单描述一下单例模式的应用场景:
单例模式能保证在一个JVM中,对象只有一个实例存在。正是由于这个特点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。我们具体问题具体分析吧。
EOF