文章内容
一、什么是单例模式
单例模式简单来说,就是要保证一个类在进程内有且只有一个实例对象。这个类对外提供了唯一获取其实例对象的静态方法,且构造方法私有化。
二、单例模式实现方式
- 懒汉式(4种)
- 饿汉式(2种)
- 静态内部类(1种)
- 枚举(1种)
三、懒汉式
1、方式一
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | public class SingletonDemo3 { //直接初始化对象 private static SingletonDemo3 instance; //构造方法私有化 private SingletonDemo3(){} // public static SingletonDemo3 getInstance( ){ if (instance== null ){ instance = new SingletonDemo3(); } return instance; } } |
1)缺点
并发场景时,线程不安全。
2)结论
不推荐使用。
2、方式二
针对实现方式一来进行升级,加入线程同步锁synchronized,保证线程安全。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | public class SingletonDemo4 { //直接初始化对象 private static SingletonDemo4 instance; //构造方法私有化 private SingletonDemo4(){} // public static synchronized SingletonDemo4 getInstance( ){ if (instance== null ){ instance = new SingletonDemo4(); } return instance; } } |
1)缺点
由于synchronized加在了方法上,并发场景下都要获取锁,性能较差。
2)结论
不推荐使用。
3、方式三(双重检查+同步锁)
针对实现方式二进行升级,将synchronized放在方法代码块中,保证并发性能。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | public class SingletonDemo5 { //直接初始化对象 private static SingletonDemo5 instance; //构造方法私有化 private SingletonDemo5(){} // public static SingletonDemo5 getInstance( ){ if (instance== null ){ synchronized (SingletonDemo5. class ){ instance = new SingletonDemo5(); } } return instance; } } |
1)缺点
由于java 虚拟机会对class进行指令重排序(JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能),在JVM中初始化对象分为三步:
- 为初始化对象分配内存空间;
- 初始化实例对象;
- 将初始化对象指定分配好的内存空间。
在发生指令重排序后,执行顺序为:1->3->2 。并发场景下,可能线程1创建单例时执行了1->3,线程2发现单例已创建,其实得到的是未初始化完成的对象,导致NPE异常。
2)结论
不推荐使用。
4、方式四(双重检查+同步锁+volatile)
针对实现方式三存在的问题进行升级,在单例对象变量前加入volatile关键字修饰,防止指令重排序。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | public class SingletonDemo6 { //直接初始化对象 private static volatile SingletonDemo6 instance; //构造方法私有化 private SingletonDemo6(){} // public static SingletonDemo6 getInstance( ){ if (instance== null ){ synchronized (SingletonDemo6. class ){ if (instance == null ){ instance = new SingletonDemo6(); } } } return instance; } } |
1)结论
推荐使用。
四、饿汉式
饿汉式,简单来说,在JVM加载类的时候就已经创建了对象,典型的以空间换时间。
1、方式一
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | public class SingletonDemo1 { //直接初始化对象 private static SingletonDemo1 instance = new SingletonDemo1(); //构造方法私有化 private SingletonDemo1(){} // public static SingletonDemo1 getInstance( ){ return instance; } } |
2、方式二
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | public class SingletonDemo2 { //定义初始化对象 private static SingletonDemo2 instance; //构造方法私有化 private SingletonDemo2(){} // 静态代码块初始化 static { instance = new SingletonDemo2(); } public static SingletonDemo2 getInstance( ){ return instance; } } |
1)优点
效率高,线程安全。
2)缺点
若是单例对象过多,比较浪费内存。
3)结论
不推荐使用
五、静态内部类
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | public class SingletonDemo7 { //构造方法私有化 private SingletonDemo7(){ } // public static SingletonDemo7 getInstance( ){ return SingletonInner.instance; } /** * 静态内部类,真正调用时才初始化单例对象 */ private static class SingletonInner{ private static SingletonDemo7 instance = new SingletonDemo7(); } } |
1、优点
延迟加载,在真正调用时进行初始化对象;线程安全;效率高(不需要同步锁)
2、结论
推荐使用。
六、枚举
1 2 3 4 5 | public enum SingletonDemo8 { // 定义一个枚举实例,就代表了该类的单例对象 INSTANCE; } |
1、优点
线程安全,效率高
2、结论
推荐使用。