Java的单例模式探索
单例模式是最常见的设计模式,也是面试必问的(基本)。你真的会写一个正确的高效的单例吗?下面分几个阶段介绍写法,直接上代码
01
public class SingletonDemo {
private static SingletonDemo instance = null;
private SingletonDemo() {
}
/**
* 原始
*
* @return
*/
public static SingletonDemo getInstance() {
if (instance == null) {
instance = new SingletonDemo();
}
return instance;
}
}
以上写法在高并发情况下会创建多个对象实例。
02
如果存在并发问题,那首先想到的是加锁。
public class SingletonDemo {
private static SingletonDemo instance = null;
private SingletonDemo() {
}
/**
* 方法同步锁
*
* @return
*/
public synchronized static SingletonDemo getInstance() {
if (instance == null) {
instance = new SingletonDemo();
}
return instance;
}
}
以上写法可以避免并发问题。但是效率不好,原因:在高并发环境中,如果此类已创建对象,后来的并发请求也要争抢锁后才能获取对象实例,最好创建了对象直接返回就好。
03
关于效率问题,我想到用此方式解决。方法不加synchronized关键字,在创建对象的代码处加synchronized
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() {
}
/**
* 创建对象时同步锁
*
* @return
*/
public static SingletonDemo getInstance() {
if (instance == null) {
//[1]
synchronized (SingletonDemo.class) {
instance = new SingletonDemo();
}
}
return instance;
}
}
以上写法,在成员变量上加了 volatile关键字,目的是保证线程间的可见性,及禁止指令重排序。
但还是有问题,[1] 处,2个线程同时运行到1处,线程-1拿到锁并创建成功返回,并释放锁,线程-2拿到锁又会创建个新的对象实例,这样就破坏了单例。
04
终极写法(大佬有更好写法请指教 哈哈)
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() {
}
/**
* 终极
*
* @return
*/
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}
}
这样就完美解决了 03 的问题。谢谢~
关于volatile关键字:
- 解决可见性:线程改变变量值后会立即写入主存,其他线程立即可见。
- 禁止重排序:在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用(重排序 JIT编译器优化),此时别的线程得到的是一个还会初始化的对象,这样会导致系统崩溃。
参考:
https://www.iteye.com/topic/652440
_