单例模式
1.作为23种设计模式之一,单例模式的应用还算是比较广泛,无论第三方库还是日常的开发。总能看到单例模式的身影,对于各种不同的实现是有所差异的。在日常开发过程中,看似简单的问题处理到极致还是需要考虑多个方面的因素的,多线程下的安全性、高性能、懒加载。。。
饿汉式
1 | public final class SingleTon { |
1.INSTANCE
作为类的实例直接初始化,当SingleTon
被主动使用时,可以保证在多线程下仅被实例化一次,但是一个如果类的成员属性过多显然比较重。另外不能实现懒加载。
懒汉式
1 | public final class SingleTon { |
1.使用时被创建,避免提前创建。但是存在缺陷,如果在多线程的程序下,当两个线程同时发现INSTANCE == null
,导致INSTANCE
被实例化了两次,这样就无法保证单例的唯一性。
同步方法+懒汉式
1 | public final class SingleTon { |
1.解决了多线程情况下普通懒汉式单例的可能存在不唯一性,由于synchronized
的排他性,作为较重量级的锁,但同时引入了另一个问题–性能较低。
DC
1 | public final class SingleTon { |
1.相比于同步方法+懒汉式的单例模式,DoubleCheck解决了首次加载时加锁,当实例存在后,多线程获得实例时则不在通过锁方法,直接获得。解决了性能问题。但是考虑一种情况如上testObj
,存在这样一种情况,多个线程调用getInstance()
方法时,显然只有一个线程会获得锁而进如初始化的后续操作。由于JVM
在实例化时有可能存在指令重排序,也即INSTANCE
被先初始化完成而testObj == null
。此时如果有另外的线程访问testObj
则会出现空指针异常,(INSTANCE
已经被实例化完成)。
DC + Volatitle
防止指令重排:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public final class SingleTon {
private volatile static SingleTon INSTANCE = null;
public Object testObj;
private SingleTon() {
//testObj初始化操作
}
public static SingleTon getInstance() {
if (null == INSTANCE) {
synchronized (SingleTon.class) {
if (null == INSTANCE) {
INSTANCE = new SingleTon();
}
}
}
return INSTANCE;
}
}
静态内部类Holder
1 | public final class SingleTon { |
1.借助类的加载机制,SingleTon
中没有INSTANCE
的实例,而存在于其静态内部类Holder
中。当singleton在类的初始化过程中并不会创建SingleTon的实例,而Holder中的静态变量SingleTon(且被实例化),当holder被主动调用时会创建SingleTon的实例。SingleTon的创建过程在编译时期的clinit()
中,此为同步方法,保证了可见行,有序性。Holder
模式应用比较广泛,没有明显的缺点。
ENUM 枚举
1 | public final class SingleTon { |
1.改进了普通枚举不支持懒加载的缺陷。枚举的优点:-、枚举类型不能被继承;二、线程安全并且只会被实例化一次。