单例模式的七种写法
本文思维导图。
先将引用lazySingleTon置为null,等到需要时才创建对象并将引用指向它。public class LazySingleTon {
private static LazySingleTon lazySingleTon = null;
private LazySingleTon() {}
public static LazySingleTon getLazySingleTon() {
if (lazySingleTon == null)
lazySingleTon = new LazySingleTon();
return lazySingleTon;
}
}
此写法线程不安全,因为多个线程同时执行 if (lazySingleTon == null) 时,条件成立,多个线程会继续执行下面创建对象的代码,此时就不是单例,而是“多例”了。
02懒汉变种
不知道该写法的名字,暂且就叫它懒汉变种吧。public class LazySingleTon {
private static LazySingleTon lazySingleTon = null;
private LazySingleTon() {}
public static synchronized LazySingleTon getLazySingleTon() {
if (lazySingleTon == null)
lazySingleTon = new LazySingleTon();
return lazySingleTon;
}
}
它使用synchronized关键字对getLazySingleTon()加锁,这样每个时刻只能有一个线程执行该方法,解决了懒汉模式中多线程同时执行的问题,此写法线程安全。
饿汉写法,类SingleTonTest被加载时就会初始化singleTonTest对象,保证了系统中只有一个该类的实例对象,线程安全。public class SingleTonTest {
private static SingleTonTest singleTonTest = new SingleTonTest();
private SingleTonTest() {}
public static SingleTonTest getInstance() {
return singleTonTest;
}
}
其实和饿汉写法差不多,只不过将创建对象放进了静态代码块。线程安全。public class SingleTonTest {
private static SingleTonTest singleTonTest = null;
static {
singleTonTest = new SingleTonTest();
}
private SingleTonTest() {
}
public static SingleTonTest getInstance() {
return singleTonTest;
}
}
双重检测机制在懒汉写法的基础上使用了双重判空+synchronized+volatile,线程安全。public class DoubleLockSingleTon {
private static volatile DoubleLockSingleTon dlst = null;
private DoubleLockSingleTon(){}
public DoubleLockSingleTon getDlst(){
if (dlst==null){
synchronized (DoubleLockSingleTon.class){
if(dlst==null){
dlst = new DoubleLockSingleTon();
}
}
}
return dlst;
}
}
注意两点:
- synchronized锁住的是DoubleLockSingleTon类对象,而不是实例对象。
- volatile关键字一定要有,它在此处的主要作用是禁止指令重排序,否则就不是绝对的线程安全了。
参照饿汉模式,此写法相当于将创建对象的代码放进了静态内部类中,这样一来,就实现了懒加载。public class StaticInnerClass {
private StaticInnerClass(){}
static class InnerClassSingleTon{
private static final StaticInnerClass sic = new StaticInnerClass();
}
public static StaticInnerClass getSic(){
return InnerClassSingleTon.sic;
}
}
也就是说当类StaticInnerClass被加载时,单例对象不一定会被创建,只有调用了getSic()时,类InnerClassSingleTon才会被加载,然后创建单例对象。线程安全。然而,该写法虽线程安全,但是可通过反射来创建多个实例对象。解决方法是使用枚举。
不得不说,使用枚举实现单例模式代码非常简洁。它有两个优点:public enum SingleTon {
INSTANCE;
}
- 线程安全。
- 能防止使用反射创建多个对象。
但是此写法不常见,想必大家最熟悉的写法就是懒汉和饿汉了...当然,我也是。
扫码关注,一起学习
BeCoder
评论