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 ...
随机推荐
- 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列表展示和选择处理
我们在WPF应用端的界面中,使用lepoco/wpfui 来做主要的入口框架,这个项目它的菜单内置了不少图标,我们需要在动态菜单的配置中,使用它作为图标的展示处理,本篇随笔介绍如何基于图标枚举集合进行 ...
- 使用Spring Integration接收TCP与UDP请求
1. 简介 Spring Integration 是一个开源的项目,它是 Spring 生态系统的一部分,旨在简化企业集成(Enterprise Integration)的开发.它提供了一种构建消息驱 ...
- Kubernetes网络
kubernetes-Service 1.service存在的意义 1.防止破的失联(服务发现) 2.定义一组pod的访问策略(提供负载均衡) 2.pod与service的关系 1.通过lablel- ...
- html5学习内容-5
(一)文字环绕排版 文字环绕图形 shape-outside属性 margin-box:外边距环绕 padding-box:内边距环绕 border-box:边框环绕 content-box:内容环绕 ...
- JavaScript 语法:语法约定与程序调试
作者:WangMin 格言:努力做好自己喜欢的每一件事 JavaScript 语法约定 1.大小写的区分 1). JavaScript的关键字,永远都是小写的: 2). 内置对象,如Math和Date ...
- SNN_文献阅读_Effective and Efficient Computation with Multiple-timescaleSpiking Recurrent Neural Networks
Adaptive SRNN 基于多时间尺度脉冲循环神经网络的高效计算(SRNN) 中心思想: 使用替代梯度进行训练,克服SNN中梯度不连续的问题. 在PyTorch中直接使用BPTT进行训练. 结构 ...
- STL unordered类容器浅谈
一个代码: #include<cstdio> #include<vector> #include<functional> #include<algorithm ...
- 【LOJ NOI Round#2 Day1 T1】单枪匹马(矩阵乘法)
题目传送门 操作二要求的东西是一个循环迭代的东西,手推相邻两项找下规律,发现相邻两项的分子分母间含有线性关系,考虑用矩阵乘法求解.对于 \([1,n]\)的询问,从后往前倒推, \(x_{n-1}=a ...
- 2019-2020 ICPC Southwestern European Regional Programming Contest (SWERC 2019-20) L题(SG+状压)
题意:给定一个N*N的表格,其上有三种类型的方格:坚实的地面.潮湿的区域和保护区.连通的湿区方格形成湿区,当两个正方形共用一条边时,它们被认为是连接的.每个湿区必须连接到网格的左右两侧,并且不包含超过 ...
- python的列表、元组
列表(list):是Python中最常用的数据类型之一,字符串里面包含元素的是 一个个的字符,并且字符串是不可能更改的,然而列表不一样,他的每个元素都 可以是任何python类型,而且是可以被更改的 ...