Ensure a class only has one instance, and provide a global point of access to it.
确保一个类只有一个实例,并且提供一个全局指针访问它
各种单例类的共同点:
- 私有构造器
- 私有的静态实例变量
- public的静态方法,返回单例对象
单例1
/**
* 基础单例类
*/
public class BasicSingleton {
private static BasicSingleton instance;
// 私有构造器,确保不会在该类之外实例化
private BasicSingleton() {
}
public static BasicSingleton getInstance() {
// Lazy initialization 懒加载
if (instance == null) {
instance = new BasicSingleton();
}
return instance;
}
}
注意构造器是private的,该单例的问题:不是线程安全的,当有多个线程调用getInstance()
方法时,可能会产生多个实例。
单例2 - 线程安全 - 粗粒度
/**
* 线程安全单例
*/
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
// 私有构造器,确保不会在该类之外实例化
private ThreadSafeSingleton() {
}
// synchronized方法
public static synchronized ThreadSafeSingleton getInstance() {
// Lazy initialization 懒加载
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
线程安全单例,该单例的问题:在多线程情况下,会在getInstance()
方法处形成阻塞。
单例3 - 线程安全 - 细粒度
/**
* 加载线粒度的锁
*/
public class ThreadSafeSingleton2 {
private static ThreadSafeSingleton2 instance;
// 私有构造器,确保不会在该类之外实例化
private ThreadSafeSingleton2() {
}
public static ThreadSafeSingleton2 getInstance() {
if (instance == null) {
// check 1
synchronized (ThreadSafeSingleton2.class) {
// double checked locking,再次判空是需要的
// check 2
if (instance == null) {
instance = new ThreadSafeSingleton2();
}
}
}
return instance;
}
}
首先要明白细粒度的意思,单例2 - 线程安全 - 粗粒度 例子中,每次进入getInstance()
方法都需要同步,太浪费。这个例子中,判断完是否为空后再同步,这样大家需要同步的机会就大大减少了。同时要注意,再次判断是否为空是需要的,如果两个线程同时到达check 1,如果没有check 2的判空,就会产生两个实例。
单例4 - Eager initialization
/**
* 提前初始化单例
*/
public class EagerInitializedSingleton {
// 类加载即创建单例
private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
// 私有构造器,确保不会在该类之外实例化
private EagerInitializedSingleton() {
}
public static EagerInitializedSingleton getInstance() {
return instance;
}
}
该单例是线程安全的,但问题是即使在不需要实例时,也被初始化了。
单例5 - 静态块单例
/**
* 静态块单例
*/
public class StaticBlockSingleton {
private static StaticBlockSingleton instance;
private StaticBlockSingleton() {
}
// 静态块的作用是,添加异常处理
static {
try {
instance = new StaticBlockSingleton();
} catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
public static StaticBlockSingleton getInstance() {
return instance;
}
}
该单例的问题同 单例4 - Eager initialization,即使在不需要实例时也被初始化了,不同的是增加了异常处理的机会。
单例6 - Bill Pugh - 最佳实践
/**
* 通过内部静态类来避免提前实例化和线程安全问题,
* 从而达到只在需要时才被加载并且没有线程安全问题
*/
public class BillPughSingleton {
// 私有构造器,确保不会在该类之外实例化
private BillPughSingleton() {
}
// 内部静态类
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
// 此时才会加载SingletonHelper类,然后创建单例对象
return SingletonHelper.INSTANCE;
}
}
内部静态类,只有在使用时才会被加载,然后创建单例。避免了提前创建和线程安全的问题。
参考:Java Singleton Design Pattern Best Practices with Examples