单例模式的七种写法
本文思维导图。

先将引用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
评论
