单例模式
- 当系统中对于某个类,只需要有一个实例化对象时,可以使用单例模式。
单例模式实现方式
单例模式的实现主要有两个特征
- 私有化构造方法,让外部不能直接通过构造方法创建对象实例;
- 提供一个静态的公共方法( 例如下方的getInstance() ),外部通过这个方法获取对象实例。
在系统启动时即创建--俗称饿汉式
public class Singleton {
private Singleton() {
}
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
第一次调用时才创建--俗称懒汉式
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式单例模式存在的问题
在并发编程的场景下,如果多个线程同时调用getInstance()方法,可能会存在创建多个对象实例的情况,为了避免这种错误的问题产生,可以使用锁来解决问题。
1. 给方法加锁
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
- 这种方式开销过大,每个线程进入方法前都要获得锁;如果实例已经被创建,访问时直接返回即可,不存在线程安全问题
2. 锁代码块
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
- 这种方式的好处是:当实例已经创建时,线程访问时就不需要再获取锁,可以直接返回。实例不存在时,保证只有单个线程在创建实例
但是这种加锁方式依然存在问题,假如有两个线程A,B调用getInstance()方法,在线程A还没有实例化完成时,线程B也可以通过if判断进入内部,等待锁释放并创建对象,此时对象依然创建了多个。
temp.png
为了解决这种问题,于是提出了以下方式。
3. DCL(Double Check Lock)双重检查锁
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
- 获取锁后,在进行一次为空判断,这样就可以解决上面提出的问题
DCL的方式看起来似乎完美无缺,但遗憾的是,其中依然存在问题。在说明问题前,先了解下什么是“指令重排序”
-
指令重排序
JVM在执行代码时,对没有数据依赖的操作步骤,可能会以指令重排序的方式执行代码,提高执行效率。如下图:
cpx.png
所以DCL模式也会出现以下问题
dcl.png
因此,线程可能会获取到一个未初始化完成的对象
dcl1.png
4. 最终写法
- 使用volatile修饰singleton
volatile可以禁止指令重排序,保证内存可见性。具体实现原理参考《java并发编程的艺术》一书。限于篇幅,不再说明。
public class Singleton {
private Singleton() {
}
private volatile static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
网友评论