单例模式是一种对象创建模式,用于产生一个类的具体事例。使用单例模式可以确保整个系统中单例类只产生一个实例。有下面两大好处:
创建方式:
| 角色 | 作用 |
|---|---|
| 单例类 | 提供单例的工厂,返回类的单例实例 |
| 使用者 | 获取并使用单例类 |
类基本结构:
public class HungerSingleton {
//1.饿汉式
//私有构造器
private HungerSingleton() {
System.out.println("create HungerSingleton");
}
//私有单例属性
private static HungerSingleton instance = new HungerSingleton();
//获取单例的方法
public static HungerSingleton getInstance() {
return instance;
}
}
public class Singleton {
//2.1简单懒汉式(线程不安全)
//私有构造器
private Singleton() {
System.out.println("create Singleton");
}
//私有单例属性[初始化为null]
private static Singleton instance = null;
//获取单例的方法
public static Singleton getInstance() {
if(instance == null) {
//此处instance实例化
//首次调用单例时会进入 达成延时加载
instance = new Singleton();
}
return instance;
}
}
public class Singleton {
//2.2简单懒汉式(线程安全)
//私有构造器
private Singleton() {
System.out.println("create Singleton");
}
//私有单例属性[初始化为null]
private static Singleton instance = null;
//获取单例的方法 将此方法使用synchronized关键字同步
public static synchronized Singleton getInstance() {
if(instance == null) {
//此处instance实例化
//首次调用单例时会进入 达成延时加载
instance = new Singleton();
}
return instance;
}
}
,使不用每次调用geInstance()方法时候都会去竞争锁?
DCL(Double Check Locking)双重检测 就是这样一种实现方式。
传统DCL:
public class DCLLazySingleton {
//3.DCL
//私有构造器
private DCLLazySingleton() {
System.out.println("create DCLLazySingleton");
}
//step1 私有单例属性[初始化为null] volatile 保证内存可见性 防止指令重排
private static volatile DCLLazySingleton instance = null;
//获取单例的方法
public static DCLLazySingleton getInstance() {
//这里判null 是为了在instance有值时,不进入加锁的代码块,提高代码性能。
if(instance == null) {
//缩小锁范围 由于是静态方法方法调用的时候不依赖于实例化的对象 加锁只能使用类
synchronized (DCLLazySingleton.class) {
//这里判null 是为了配合volatile解决多线程安全问题
if(instance == null) {
instance = new DCLLazySingleton();
}
}
}
return instance;
}
}
传统DCL(未使用 volatile 或在JDK1.8之前版本)面临的问题:
在线程1初始化对象可能并未完成,但是此时已经instance对象已经不为null。(已经分配了内存,但是构造方法还未执行完【 可能有一些属性的赋值未执行 】)
此时线程2再获取instance 则不为null 直接返回。那么此时线程2获取的则为‘构造方法未执行完的instance对象’。则不能保证线程安全。
解决方式:
参考:
public class StaticSingleton {
//私有构造器
private StaticSingleton() {
System.out.println("create StaticSingleton!");
}
//获取单例的方法
public static StaticSingleton getInstance() {
return SingletonHolder.instance;
}
//静态内部类 持有单例 作为静态属性。
//由于只有在访问属性时才会加载静态类初始化instance。所以实现了懒加载。且由于JVM保证了类的加载为线程安全,所以为线程安全的。
private static class SingletonHolder {
//私有单例属性
private static StaticSingleton instance = new StaticSingleton();
}
}
public enum EnumSingleton {
INSTANCE();
EnumSingleton() {
System.out.println("create EnumSingleton");
}
}
上述的单例实现方式还是会面临一些特殊情况不能保证唯一实例:
private Object readResolve () {
//返回当前对象
return instance;
}
由于上述两情况比较特殊,所以没有特别关注。
《Java程序性能优化》 -葛一鸣 等编著