大家好,我是冰河~~
今天给大家介绍《Java极简设计模式》的第01章,单例设计模式(Singleton),多一句没有,少一句不行,用最简短的篇幅讲述设计模式最核心的知识,好了,开始今天的内容。
看几个单例对象的示例代码,其中有些代码是线程安全的,有些则不是线程安全的,需要大家细细品味,这些代码也是冰河本人在高并发环境下测试验证过的。
这个类是懒汉模式,并且是线程不安全的
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程不安全的 */public class SingletonExample1 { private SingletonExample1(){ } private static SingletonExample1 instance = null; public static SingletonExample1 getInstance(){ //多个线程同时调用,可能会创建多个对象 if (instance == null){ instance = new SingletonExample1(); } return instance; }}
饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的 */public class SingletonExample2 { private SingletonExample2(){ } private static SingletonExample2 instance = new SingletonExample2(); public static SingletonExample2 getInstance(){ return instance; }}
懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐 */public class SingletonExample3 { private SingletonExample3(){ } private static SingletonExample3 instance = null; public static synchronized SingletonExample3 getInstance(){ if (instance == null){ instance = new SingletonExample3(); } return instance; }}
懒汉模式(双重锁同步锁单例模式),单例实例在第一次使用的时候进行创建,但是,这个类不是线程安全的!!!!!
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 懒汉模式(双重锁同步锁单例模式) * 单例实例在第一次使用的时候进行创建,这个类不是线程安全的 */public class SingletonExample4 { private SingletonExample4(){ } private static SingletonExample4 instance = null; //线程不安全 //当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令: //1.memory = allocate() 分配对象的内存空间 //2.ctorInstance() 初始化对象 //3.instance = memory 设置instance指向刚分配的内存 //单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。 // 指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。 //如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行: //1.memory = allocate() 分配对象的内存空间 //3.instance = memory 设置instance指向刚分配的内存 //2.ctorInstance() 初始化对象 //假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){ 处, //如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance; //而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。 public static SingletonExample4 getInstance(){ if (instance == null){ synchronized (SingletonExample4.class){ if(instance == null){ instance = new SingletonExample4(); } } } return instance; }}
线程不安全分析如下:
当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令:
1.memory = allocate() 分配对象的内存空间 2.ctorInstance() 初始化对象 3.instance = memory 设置instance指向刚分配的内存
单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。
指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。
如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行:
1.memory = allocate() 分配对象的内存空间 3.instance = memory 设置instance指向刚分配的内存 2.ctorInstance() 初始化对象
假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){ 处,如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance;而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。
懒汉模式(双重锁同步锁单例模式)单例实例在第一次使用的时候进行创建,这个类是线程安全的,使用的是 volatile + 双重检测机制来禁止指令重排达到线程安全
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 懒汉模式(双重锁同步锁单例模式) * 单例实例在第一次使用的时候进行创建,这个类是线程安全的 */public class SingletonExample5 { private SingletonExample5(){ } //单例对象 volatile + 双重检测机制来禁止指令重排 private volatile static SingletonExample5 instance = null; public static SingletonExample5 getInstance(){ if (instance == null){ synchronized (SingletonExample5.class){ if(instance == null){ instance = new SingletonExample5(); } } } return instance; }}
饿汉模式,单例实例在类装载的时候(使用静态代码块)进行创建,是线程安全的
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的 */public class SingletonExample6 { private SingletonExample6(){ } private static SingletonExample6 instance = null; static { instance = new SingletonExample6(); } public static SingletonExample6 getInstance(){ return instance; }}
枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的
package io.binghe.concurrency.example.singleton;/** * @author binghe * @version 1.0.0 * @description 枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的 */public class SingletonExample7 { private SingletonExample7(){ } public static SingletonExample7 getInstance(){ return Singleton.INSTANCE.getInstance(); } private enum Singleton{ INSTANCE; private SingletonExample7 singleton; //JVM保证这个方法绝对只调用一次 Singleton(){ singleton = new SingletonExample7(); } public SingletonExample7 getInstance(){ return singleton; } }}
(责任编辑:探索)
全球分布式算力共享鼻祖寻找外星人近 21 年,一切都是徒劳?
碧桂园服务(06098.HK)公布:拟收购蓝光嘉宝服务(02606.HK)64.62%股权 明日复牌
国家统计局:10月份货物进出口总额33357亿元 出口19408亿元