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;
}
}
}
7种创建方式,带你理解Java的单例模式的更多相关文章
- 【转载】 Java中String类型的两种创建方式
本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...
- 2019年6月14日 Web框架之Django_07 进阶操作(MTV与MVC、多对多表三种创建方式、前后端传输数据编码格式contentType、ajax、自定义分页器)
摘要 MTV与MVC 多对多表三种创建方式 ajax ,前后端传输数据编码格式contentType 批量插入数据和自定义分页器 一.MVC与MTV MVC(Model View Controller ...
- 精读JavaScript模式(四),数组,对象与函数的几种创建方式
一.前言 放了个元旦,休息了三天,加上春运抢票一系列事情的冲击,我感觉我的心已经飞了.确实应该收收心,之前计划的学习任务也严重脱节了:我恨不得打死我自己. 在上篇博客中,笔记记录到了关于构造函数方面的 ...
- JavaScript 闭包的详细分享(三种创建方式)(附小实例)
JavaScript闭包的详细理解 一.原理:闭包函数--指有权访问私有函数里面的变量和对象还有方法等:通俗的讲就是突破私有函数的作用域,让函数外面能够使用函数里面的变量及方法. 1.第一种创建方式 ...
- Django-多对多关系的三种创建方式-forms组件使用-cookie与session-08
目录 表模型类多对多关系的三种创建方式 django forms 组件 登录功能手写推理过程 整段代码可以放过来 forms 组件使用 forms 后端定义规则并校验结果 forms 前端渲染标签组件 ...
- 多对多三种创建方式、forms组件、cookies与session
多对多三种创建方式.forms组件.cookies与session 一.多对多三种创建方式 1.全自动 # 优势:不需要你手动创建第三张表 # 不足:由于第三张表不是你手动创建的,也就意味着第三张表字 ...
- 多对多的三种创建方式-forms相关组件-钩子函数-cookie与session
多对多的三种创建方式 1.全自动(推荐使用的**) 优势:第三张可以任意的扩展字段 缺点:ORM查询不方便,如果后续字段增加更改时不便添加修改 manyToManyField创建的第三张表属于虚拟的, ...
- ajax补充知识点、多对多外键的三种创建方式、django内置序列化组件、批量操作数据、分页器推导思路与自定义分页器的使用、form组件
今日内容 ajax补充说明 主要是针对回调函数args接收到的响应数据 1.后端request.is_ajax() 用于判断当前请求是否由ajax发出 2.后端返回的三板斧都会被args接收不在影响整 ...
- 12月20日内容总结——ajax补充知识点、多对多外键的三种创建方式、django内置序列化组件、批量操作数据、分页器推导思路与自定义分页器的使用、form组件
目录 一.ajax补充说明 二.多对多三种创建方式 三.django内置序列化组件(drf前身) 四.批量操作数据 五.分页器思路 六.自定义分页器的使用 七.form组件 forms组件介绍 For ...
- 面向面试编程——javascript对象的几种创建方式
javascript对象的几种创建方式 总共有以下几个模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.混合构造函数和原型模式 5.动态原型模式 6.寄生构造函数模式 7.稳妥构造函数模式 1 ...
随机推荐
- 若依(ruoyi)开源系统-多数据源问题踩坑实录
内容概要 上一节内容 介绍了用开源系统若依(ruoyi)搭建页面的过程.在实际项目中,经常遇到多数据源后者主从库的情况.本节记录若依多数据源配置过程中遇到的问题排查过程. 背景描述 1.上一节在r ...
- IEEE754浮点数与字节数互转工具
前段时间做的一个小工具,分享一下. 提供浮点数和字节的相互转换. 下载
- 20.7 OpenSSL 套接字SSL加密传输
OpenSSL 中的 SSL 加密是通过 SSL/TLS 协议来实现的.SSL/TLS 是一种安全通信协议,可以保障通信双方之间的通信安全性和数据完整性.在 SSL/TLS 协议中,加密算法是其中最核 ...
- or等价改写union SQL案例
同事找我优化一些SQL,其中有个SQL比较经典,拿出来分享给大家,从原来执行2分钟,到1.4S出结果. -- 原SQL SELECT count (*) FROM ( SELECT DISTINCT ...
- C/C++ __builtin 超实用位运算函数总结
以 __builtin 开头的函数,是一种相当神奇的位运算函数,下面本人盘点了一下这些以 __builtin 开头的函数,希望可以帮到大家. 1 __builtin_ctz( ) / __buitli ...
- Linux Media 子系统链路分析
一.概述 Media 子系统是一个用于处理多媒体设备的框架,它提供了一组 API 和驱动程序,用于管理和控制视频.音频和其他多媒体设备.而 V4L2 是 media 子系统的一部分,用于处理视频相关的 ...
- 02Java学习_注意事项和学习方法
02_Java 开发注意事项细节和学习方法 目录 02_Java 开发注意事项细节和学习方法 注意事项 学习方法 注意事项 .java 是 Java 文件的拓展名.源文件的基本组成部分是类--clas ...
- 一个小型markdown读写编辑器推荐
在作业完成时我和很多同学们发现并没有很好地利用博客园里面自带的markdown标记语法的形式完成作业 因此我在网上自己搜索发现了这款软件 下载地址 在帮助里可以学到一些简单的使用形式 可以方便编辑已有 ...
- 神经网络入门篇:详解随机初始化(Random+Initialization)
当训练神经网络时,权重随机初始化是很重要的.对于逻辑回归,把权重初始化为0当然也是可以的.但是对于一个神经网络,如果把权重或者参数都初始化为0,那么梯度下降将不会起作用. 来看看这是为什么. 有两个输 ...
- 从物理机到K8S:应用系统部署方式的演进及其影响
公众号「架构成长指南」,专注于生产实践.云原生.分布式系统.大数据技术分享. 概述 随着科技的进步,软件系统的部署架构也在不断演进,从以前传统的物理机到虚拟机.Docker和Kubernetes,我们 ...