全局唯一
场景
- 全局唯一的共享资源
静态版本
实现
1 | public class Singleton{ |
使用
1 | Singleton ins = Singleton.getInstance(); |
特征
- private构造,禁止外部创建
- private static final实例,类加载初始化
- public static获取实例方法
优缺点
- 利用static特性实现单例,类加载初始化,不能延迟加载
- 如果支持序列化接口,还要额外考虑readResolve()以及transient
如果是支持序列化,还要阻止反序列化可能产生新实例
1 | public class Singleton implements Serializable{ |
更严格的话,反射也可以破坏单例,需要阻止构造函数被调用
1 | private Singleton(){ |
枚举版本
实现
1 | public enum Singleton{ |
使用
1 | Singleton ins = Singleton.INSTANCE; |
优缺点
- 利用枚举特性实现单例,极简
延迟加载版本
多线程共享的资源想被用到时再初始化,同时要避免资源被不同线程反复初始化
整体同步
整个方法同步,同步机制保证了排他性和可见性
但是保护范围过大,每次调用都进行同步,即使是完成初始化后,性能差
1 | public synchronized static Resource getResource(){ |
Double-Check
内部加锁,双重检查
第一层检查能够使得初始化完成后,无需再保护
第二层检查在锁内,避免再次初始化
1 | public static Resource getResource() { |
注意仅仅是上述方法并不能保证正确
resource是引用类型,由于java内存模型允许无序写入,可能先分配空间,引用指向空间,再进行构造
因此变量变为非null时,构造函数可能还没执行,此时共享变量指向一个尚未完成的引用, 其他线程使用可能会调用到尚未完成构造的实例进而出现问题
Java5后可以搭配volatile
保证完整
1 | private volatile static Resource resource; |
这里没有final, 因为不是在构造函数里赋值
静态内类
再包一层,既利用了静态特性,又不至于一开始就加载
静态内类定义时不会被加载,只有使用内部的静态变量时才会被加载,这个加载过程JVM会保证唯一性,无需保护,另外final
保证了变量被安全发布
1 | public class Singleton { |
应用
java.lang.Runtime
一分也是爱~
版权声明
This site by Linest is licensed under a Creative Commons BY-NC-ND 4.0 International License.
由Linest创作并维护的博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。
本文永久链接:http://linest.github.io/2016/11/12/java-pattern-singleton/