这篇文章是学习单例模式的笔记总结,主要内容包括了懒汉式,饿汉式,双重校验锁,枚举,静态内部类五种单例模式,强烈推荐使用原生记事本默写一遍。
懒汉式
1 | public class Singleton { |
特点:lazy,非线程安全,简单
饿汉式
1 | public class Singleton { |
特点:not lazy,线程安全,简单高效,但类加载时就初始化,即使暂不需要,浪费内存
双重校验锁(Double-checked locking)
1 | public class Singleton { |
特点:Lazy,线程安全,多线程下能保持高性能
为什么需要volatile?
加volatile关键字生成的汇编指令会多一个lock前缀指令,相当于一个内存屏障。
- 禁止进行指令重排序。
- 保证了不同线程对该变量操作的可见性。具体来说是强制将对缓存的修改立即写入主存,
如果是写操作,它会导致其他CPU中对应的缓存行无效。
为什么需要双重if校验?
1 | singleton = new Singleton(); // 代码2 |
上面这行可以分解为三个操作:
1 | 1. memory = allocate(); // 分配内存 |
如果没有加入volatile关键字,可能会出现指令重排序1-2-3
或1-3-2
两种情况
对于第2种情况,线程A,B都进入getInstance方法后,线程A获得锁,执行了1,3,此时B判断instance不为null,直接返回未初始完成的对象,就会出现问题。因此需要用volatile保证指令重排序。
枚举类型
1 | public enum Singleton { |
特点:not Lazy,线程安全,实现简单,不存在反射和反序列化漏洞
静态内部类(static nested class)
1 | public class Singleton{ |
特点:Lazy,线程安全,实现相比双重校验锁简单
引申:静态内部类与内部类区别,内存泄漏
静态内部类等同于外部类的静态方法,只能访问外部类的静态变量和静态方法。