石一歌的单例模式笔记
单例模式
单例模式只能有一个实例。
单例类必须创建自己的唯一实例。
单例类必须向其他对象提供这一实例。
饿汉式
-
静态常量
public class HungryMan { // private 私有化构造方法,无法被外部实例化 private HungryMan() { } // private 自行实例化,无法被直接调用 // static 静态方法只能调用静态成员 private final static HungryMan HUNGRY = new HungryMan(); // 暴露调用方法 // static 外部无法实例化,调用方法必须使用static修饰 public static HungryMan getInstance() { return HUNGRY; } }
- 优点
- 简单,线程安全
- 缺点
- 在类装载的时候就完成实例化,容易浪费内存
- 优点
-
静态代码块
仅仅将自行实例化的操作放在了静态代码块中,与上一种无实际区别。
public class HungryManTwo { private HungryManTwo() { } private final static HungryManTwo HUNGRY; static { HUNGRY = new HungryManTwo(); } public static HungryManTwo getInstance() { return HUNGRY; } }
懒汉式
-
一代目
针对饿汉式的问题,将实例化的过程留到调用方法时,减少内存使用。
public class LazyMan { private LazyMan() { } private static LazyMan lazyMan; public static LazyMan getInstance() { if (lazyMan == null) { lazyMan = new LazyMan(); } return lazyMan; } }
- 优点
- 懒加载机制,避免浪费内存
- 缺点
- 线程不安全
- 优点
-
二代目(同步方法)
针对上一代的问题,使用synchronized同步方法,保证线程安全。
public class LazyManTwo { private LazyManTwo() { } private static LazyManTwo lazyMan; public synchronized static LazyManTwo getInstance() { if (lazyMan == null) { lazyMan = new LazyManTwo(); } return lazyMan; } }
- 优点
- 懒加载机制,避免浪费内存;线程安全
- 缺点
- 效率低
- 优点
-
三代目(同步代码块)
针对上一代的问题,将同步方法换为同步代码块,提高效率。
public class LazyManThree { private LazyManThree() { } private static LazyManThree lazyMan; public static LazyManThree getInstance() { if (lazyMan == null) { synchronized (LazyManThree.class) { lazyMan = new LazyManThree(); } } return lazyMan; } }
- 优点
- 懒加载机制,避免浪费内存;效率高
- 缺点
- 线程不安全
- 优点
-
四代目(双重检查)
针对上两代的问题,使用
DLC
双重校验加锁,在提高效率的同时保证线程安全。public class LazyManFour { private LazyManFour() { } private volatile static LazyManFour lazyMan; public static LazyManTwo getInstance() { if (lazyMan == null) {//提高效率 synchronized (LazyManFour.class) { if (lazyMan == null) {//保证线程安全 lazyMan = new LazyManFour(); } /* 初始化操作并非原子命令,可能指令重排导致npe异常 分配内存空间 初始化对象 指向分配好的内存空间 用volatile 解决指令重排 */ } } return lazyMan; } }
- 优点
- 懒加载机制,避免浪费内存;效率高;线程安全
- 优点
其他方式
-
静态内部类
public class Holder { private Holder() { } public static Holder getInstance() { return InnerClass.HOLDER; } public static class InnerClass { private static final Holder HOLDER = new Holder(); /* 只会在外部类调用内部类时,随内部类初始化加载一次,线程安全 */ } }
- 优点
- 懒加载机制,避免浪费内存;效率高;线程安全
- 优点
-
枚举类
public enum EnumSingle { INSTANCE; public EnumSingle getInstance() { return INSTANCE; } /* 注意,直接查看反编译的文件的无参构造是错误的,jad反编译文件可知,enum是有参构造 枚举单例无法被反射破坏 */ }
- 优点
- 懒加载机制,避免浪费内存;效率高;线程安全;反射无法破坏;代码简洁
- 优点
总结
一般情况下,我们只推荐后三种写法,都能做到在保证线程安全的同时,效率高,其中尤其推荐枚举写法,代码优雅,写法简单,还可以防止反射。