设计模式 - 单例模式Singleton的8种写法

设计模式 - 单例模式Singleton的8种写法_第1张图片
.
阻止别人创建第二个实例

.

总共有8种写法

.

第1种 饿汉式

设计模式 - 单例模式Singleton的8种写法_第2张图片
不算完美, 但是比较实用, 所以比较推荐
一般程序员不太会去调用Class.forName(), 也就不会触发new实例

.

第2种 饿汉式 - static代码块 ( 跟第一种是一个意思 )

设计模式 - 单例模式Singleton的8种写法_第3张图片
.

第3种 Lazy Loading 懒汉式 线程不安全

设计模式 - 单例模式Singleton的8种写法_第4张图片
.

第4种 懒汉式 - 在getInstance方法上加synchronized关键字

设计模式 - 单例模式Singleton的8种写法_第5张图片
线程安全, 但是会有长期的性能问题 ( 每次调用getInstance方法, 都会涉及加锁操作 )
.

第5种 懒汉式 synchronized代码块 线程不安全

设计模式 - 单例模式Singleton的8种写法_第6张图片
解决了初始化完成之后, 性能下降的问题, 只有在初期, 代码才会进入synchronized
但是仍然有可能会有多个线程进入synchronized同步代码块, 每次进去, 都会new一个实例, 所以也是有问题的

第6种 双重判空 + synchronized 很多人认为的完美解法

最主要的缺点就是太复杂
设计模式 - 单例模式Singleton的8种写法_第7张图片
第一个判空是有必要的, 可以阻止很多线程一上来就去获取锁, 这样就又退回到前一个解法了

截图中的instance漏加了volatile, 看吧 ! ! 多复杂 ! 容易写错 !

.

第7种 静态内部类方式 ( 第一种完美解法)

设计模式 - 单例模式Singleton的8种写法_第8张图片
在内部类里给instance直接赋值, 静态内部类必须是private的
加载外部类的时候, 不会加载内部类, 这样就实现了懒加载
加载指的是Class.forName, 但由于程序员一般不会使用Class.forName, 所以更推荐第一种写法
JVM能保证加载一个类, 只会加载一次, 所以只会new一次

第8种 枚举enum 完美中的完美(语法层面)

java创始人之一, 在Effective Java一书中的推荐

设计模式 - 单例模式Singleton的8种写法_第9张图片
.

为什么要在单例模式中防止反序列化?

前面7种解法, 都可以用反射的方式, 创建出第二个实例!!!

枚举类为什么可以防止反射?

因为枚举类没有构造方法, 即使用反射拿到class对象, 也没法调用构造方法

但是也有缺点: 写起来别扭, 还是用class写起来比较顺, 定义成enum总感觉写逻辑不太舒服

.

总结

在项目中最推荐用第一种写法, 最简单好记, 线程安全
首先程序员一般不会去调用Class.forName去加载它, 所以只是看上去像饿汉, 实际上却和懒汉差不多
另外, 程序员更不会去用反射故意去new第二个对象, 这种故意搞破坏不在考虑范围之内

第六种写法太复杂, 要记得细节点特别多, 而且也没法阻止反射的方法
第七种写法相对简单, 比第一种还是有些复杂, 而且也没法阻止反射的方法
第八种写法在语法上是完美的, 简单, 线程安全, 防止反射, 就是写起来别扭
.

你可能感兴趣的:(design,pattern,设计模式,singleton)