本文分享自华为云社区《《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的单例模式的更多相关文章

  1. 【转载】 Java中String类型的两种创建方式

    本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...

  2. 2019年6月14日 Web框架之Django_07 进阶操作(MTV与MVC、多对多表三种创建方式、前后端传输数据编码格式contentType、ajax、自定义分页器)

    摘要 MTV与MVC 多对多表三种创建方式 ajax ,前后端传输数据编码格式contentType 批量插入数据和自定义分页器 一.MVC与MTV MVC(Model View Controller ...

  3. 精读JavaScript模式(四),数组,对象与函数的几种创建方式

    一.前言 放了个元旦,休息了三天,加上春运抢票一系列事情的冲击,我感觉我的心已经飞了.确实应该收收心,之前计划的学习任务也严重脱节了:我恨不得打死我自己. 在上篇博客中,笔记记录到了关于构造函数方面的 ...

  4. JavaScript 闭包的详细分享(三种创建方式)(附小实例)

    JavaScript闭包的详细理解 一.原理:闭包函数--指有权访问私有函数里面的变量和对象还有方法等:通俗的讲就是突破私有函数的作用域,让函数外面能够使用函数里面的变量及方法. 1.第一种创建方式 ...

  5. Django-多对多关系的三种创建方式-forms组件使用-cookie与session-08

    目录 表模型类多对多关系的三种创建方式 django forms 组件 登录功能手写推理过程 整段代码可以放过来 forms 组件使用 forms 后端定义规则并校验结果 forms 前端渲染标签组件 ...

  6. 多对多三种创建方式、forms组件、cookies与session

    多对多三种创建方式.forms组件.cookies与session 一.多对多三种创建方式 1.全自动 # 优势:不需要你手动创建第三张表 # 不足:由于第三张表不是你手动创建的,也就意味着第三张表字 ...

  7. 多对多的三种创建方式-forms相关组件-钩子函数-cookie与session

    多对多的三种创建方式 1.全自动(推荐使用的**) 优势:第三张可以任意的扩展字段 缺点:ORM查询不方便,如果后续字段增加更改时不便添加修改 manyToManyField创建的第三张表属于虚拟的, ...

  8. ajax补充知识点、多对多外键的三种创建方式、django内置序列化组件、批量操作数据、分页器推导思路与自定义分页器的使用、form组件

    今日内容 ajax补充说明 主要是针对回调函数args接收到的响应数据 1.后端request.is_ajax() 用于判断当前请求是否由ajax发出 2.后端返回的三板斧都会被args接收不在影响整 ...

  9. 12月20日内容总结——ajax补充知识点、多对多外键的三种创建方式、django内置序列化组件、批量操作数据、分页器推导思路与自定义分页器的使用、form组件

    目录 一.ajax补充说明 二.多对多三种创建方式 三.django内置序列化组件(drf前身) 四.批量操作数据 五.分页器思路 六.自定义分页器的使用 七.form组件 forms组件介绍 For ...

  10. 面向面试编程——javascript对象的几种创建方式

    javascript对象的几种创建方式 总共有以下几个模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.混合构造函数和原型模式 5.动态原型模式 6.寄生构造函数模式 7.稳妥构造函数模式 1 ...

随机推荐

  1. idea修改默认maven配置

    idea修改默认maven配置 方法一 (不推荐) 打开project.default.xml文件,在其中加入如下几行配置. 代码如下 保存修改之后新建一个maven项目查看效果 方法二 新增Proj ...

  2. 【BUU刷题日记】——第一周

    [BUU刷题日记]--第一周 一.[极客大挑战 2019]PHP1 题目说自己有一个备份网站的习惯,所以要了解一下常见的网站源码备份格式及文件名: 格式:tar.tar.gz.zip.rar 文件名: ...

  3. 2023-10-18:用go语言,给定一个数组arr,长度为n,表示有0~n-1号设备, arr[i]表示i号设备的型号,型号的种类从0~k-1,一共k种型号, 给定一个k*k的矩阵map,来表示型号

    2023-10-18:用go语言,给定一个数组arr,长度为n,表示有0~n-1号设备, arr[i]表示i号设备的型号,型号的种类从0~k-1,一共k种型号, 给定一个k*k的矩阵map,来表示型号 ...

  4. PolarD&N2023秋季个人挑战—Crypto全解

    EasyRSA (额..这个题看错了不是挑战赛的.这里当做好题记录下来了) 题目信息:500 分 5 Polar币 from Crypto.Util.number import bytes_to_lo ...

  5. linux 批量替换文件内容

    1.批量查找某个目下文件的包含的内容,例如: #   grep -rn "要找查找的文本" ./ 2.批量查找并替换文件内容. #   sed -i "s/要找查找的文本 ...

  6. Kubernetes网络

    kubernetes-Service 1.service存在的意义 1.防止破的失联(服务发现) 2.定义一组pod的访问策略(提供负载均衡) 2.pod与service的关系 1.通过lablel- ...

  7. C#winform软件实现一次编译,跨平台windows和linux兼容运行,兼容Visual Studio原生界面Form表单开发

    一.背景: 微软的.net core开发工具,目前来看,winform界面软件还没有打算要支持linux系统下运行的意思,要想让c#桌面软件在linux系统上运行,开发起来还比较麻烦.微软只让c#的控 ...

  8. Mysql面试大全

    说说MySQL索引的底层数据结构 MySQL索引的底层数据结构是B+树数据结构 详细介绍一下B+树的数据结构是什么样子的 B+树有三个特性 B+树是一个平衡多叉树,与平衡二叉树的每一个节点下面最多有两 ...

  9. BABYRE

    一道SMC,第一次做 主函数的伪代码,judge函数是关键函数,不过啥都没有 发现 judge 方法是判断的主要逻辑,在第 15 行时调用判断. 但是静态分析时不能生成 judge 的伪代码. 原因是 ...

  10. vue 组件之间的自定义方法互相调用

    1,先定义一个中间通信文件js globalBus.js import Vue from 'vue'; export const globalBus = new Vue(); 2,A.vue组件的方法 ...