7种创建方式,带你理解Java的单例模式
本文分享自华为云社区《《Java极简设计模式》第01章:单例模式(Singleton)》,作者:冰 河。
单例设计模式
看几个单例对象的示例代码,其中有些代码是线程安全的,有些则不是线程安全的,需要大家细细品味,这些代码也是在高并发环境下测试验证过的。
- 代码一:SingletonExample1
这个类是懒汉模式,并且是线程不安全的
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; } }
- 代码二:SingletonExample2
饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
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; } }
- 代码三:SingletonExample3
懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐
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; } }
- 代码四:SingletonExample4
懒汉模式(双重锁同步锁单例模式),单例实例在第一次使用的时候进行创建,但是,这个类不是线程安全的!!!!!
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使用,就会出现问题。
- 代码五:SingletonExample5
懒汉模式(双重锁同步锁单例模式)单例实例在第一次使用的时候进行创建,这个类是线程安全的,使用的是 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; } }
- 代码六:SingletonExample6
饿汉模式,单例实例在类装载的时候(使用静态代码块)进行创建,是线程安全的
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; } }
- 代码七:SingletonExample7
枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的
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; } } }

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
介绍一下我们的开源“充电之旅” - 两位新晋 Apache Flink Committer 专访
本文出自字节跳动流式计算团队的方勇、胡伟华同学专访。两位同学在 Apache Flink 社区主要贡献了包括 Runtime Coordinator、Streaming Warehouse 等相关 Feature。于2023年7月正式受邀成为 Apache Flink Committer。 在软件开发的世界中,开源已成为普遍关注的话题。越来越多的企业和开发者认识到开源的重要性,并开始积极拥抱开源、贡献开源。自2017年开始,字节跳动流式计算团队开始尝试使用 Apache Flink 作为流式计算引擎,并逐步加大对开源社区的关注和投入。 近两个月来,团队方勇、胡伟华两位同学先后受邀成为 Apache Flink Committer。本文将对两位新晋 Committer 参与开源的心路历程进行专访。 我的开源参与之路 Apache Flink 是一个高性能的分布式计算框架,目前也已经是流式计算的事实标准,很大程度上推动了整个流式数据处理方面的发展。对于两位新晋 Committer 而言,Flink 在 Apache 中是不可忽视的明星项目。 作为一个非常活跃的社区,用户提出的问题很快就会...
- 下一篇
带你读论文丨S&P21 Survivalism: Living-Off-The-Land 经典离地攻击
本文分享自华为云社区《[论文阅读] (21)S&P21 Survivalism: Living-Off-The-Land 经典离地攻击》,作者: eastmount 。 摘要 随着恶意软件检测算法和方法变得越来越复杂(sophisticated),恶意软件作者也采用(adopt)同样复杂的逃避机制(evasion mechansims)来对抗(defeat)它们。 民间证据表明离地攻击技术(Living-Off-The-Land,LotL)是许多恶意软件攻击中最主要的逃避技术之一。这些技术利用(leverage)系统中已经存在的二进制文件来执行(conduct)恶意操作。 基于此,我们首次对Windows系统上使用这些技术的恶意软件进行大规模系统地调查。 在本文中,我们分析了这些本地系统的二进制文件在多个恶意软件数据集上的使用情况,这些数据集共包含31,805,549个样本。我们发现平均流行率(prevalence)为9.41%。实验结果表明,LotL技术被大量的使用,特别是在高级持久性威胁(Advanced Persistent Threat ,APT)恶意软件样本中,离地攻...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19