单例模式
日头没有辜负我们,我们也切莫辜负日头。——沈从文
代码世界中也存在以下顺口溜:
我单身,我骄傲,我为国家省套套。
我单身,我自豪,我为祖国省橡胶。
单例模式虽然简单,但真正懂的内行的人并不多,今天挑战全网最全的经典设计模式之单例模式。
1. 单例模式
定义
确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
隐藏其构造方法
属于创建型设计模式
适用场景
确保任何情况下都绝对只有一个实例
ServletContext、ServletConfig、ApplicationContext、DBPool
2. 饿汉式单例
定义
系统初始化的时候就加载,不管有没有用到这个单例。
优点
执行效率高,性能高,没有任何的锁
缺点
某些情况下,可能会造成内存浪费
能够被反射破坏
代码
public class HungrySingleton {
private static final HungrySingleton singleton = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance() {
return singleton;
}
}
3. 懒汉式单例
定义
系统初始化的时候不创建实例,只有用到的时候才创建实例。
优点
节省了内存
缺点
synchronized造成性能低下
能够被反射破坏
3.1 方法加锁写法
代码
public class LazySingleton {
private static LazySingleton singleton = null;
private LazySingleton(){
}
/**
* 版本1
* @return
*/
public static synchronized LazySingleton getInstance() {
if (null == singleton) {
singleton = new LazySingleton();
}
return singleton;
}
}
3.2 代码块加锁写法
代码
public class LazySingleton {
private static LazySingleton singleton = null;
private LazySingleton(){
}
/**
* 版本2 相比版本1优化一点点
* @return
*/
public static LazySingleton getInstance() {
synchronized (LazySingleton.class) {
if (null == singleton) {
singleton = new LazySingleton();
}
}
return singleton;
}
}
3.3 双重判断加锁写法
陷阱案例
public class LazySingleton {
private static LazySingleton singleton = null;
private LazySingleton(){
}
/**
* 版本3 双重判断
* @return
*/
public static LazySingleton getInstance() {
if (null == singleton) {
synchronized (LazySingleton.class) {
if (null == singleton) {
singleton = new LazySingleton();
}
}
}
return singleton;
}
}
版本3看起来相比版本2优化了不少,但其实这种双重判断在生产环境有一个极大的漏洞陷阱,就是指令重排序,有需要了解的可以在评论区留言。解决方案也很简单,就是 volatile 关键字。它可以限制指令重排序。
正确写法
public class LazySingleton {
private volatile static LazySingleton singleton = null;
private LazySingleton(){
}