作者:冰河

星球:http://m6z.cn/6aeFbs

博客:https://binghe.gitcode.host

文章汇总:https://binghe.gitcode.host/md/all/all.html

源码地址:https://github.com/binghe001/java-simple-design-patterns/tree/master/java-simple-design-singleton

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:介绍创建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;
}
}
}

好了,今天的内容就到这儿吧,相信大家对单例设计模式有了全新的认识,我是冰河,我们下期见~~

《Java极简设计模式》第01章:单例模式(Singleton)的更多相关文章

  1. 【Java】【设计模式 Design Pattern】单例模式 Singleton

    什么是设计模式? 设计模式是在大量的实践中总结和理论化之后的最佳的类设计结构,编程风格,和解决问题的方式 设计模式已经帮助我们想好了所有可能的设计问题,总结在这些各种各样的设计模式当中,也成为GOF2 ...

  2. Head First 设计模式 第5章 单例模式

    第5章 单例模式 1.定义:确保一个类只有一个实例,并为其创建访问点. 2.单例模式的类图: 对应的单例模式的代码: package com.ek.singleton; /** * @包名 com.e ...

  3. Java多线程编程核心技术-第6章-单例模式与多线程-读书笔记

    第 6 章 单例模式与多线程 本章主要内容 如何使单例模式遇到多线程是安全的.正确的. 6.1 立即加载 / “饿汉模式” 什么是立即加载?立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就 ...

  4. 设计模式(二)单例模式Singleton(创建型)

    几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的.您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销.再如大家最经常用的IM, ...

  5. JAVA设计模式——第 3 章 单例模式【Singleton Pattern】(转)

    这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如此的有人缘,单例就是单一.独苗的意思,那什么是独一份呢?你的思维是独一份,除此之外还有什么不能山寨的呢?我们举个比较难复制的 ...

  6. java——极简handler机制

    handler机制要做的事情: 1.把一堆从四面八方传来的message加到一个队列中,这个队列就是MessageQueue. 2.将MessageQueue中的队头Message取出,并使用这个me ...

  7. 资源对象的池化, java极简实现,close资源时,自动回收

    https://www.cnblogs.com/piepie/p/10498953.html 在java程序中对于资源,例如数据库连接,这类不能并行共享的资源对象,一般采用资源池的方式进行管理. 资源 ...

  8. .NET设计模式(1):1.1 单例模式(Singleton Pattern)

    概述 单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点. 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单 ...

  9. Effective java 第2版 - 笔记(01) 单例(Singleton)的枚举(enum)实现

    直接上代码: public enum Boss { INSTANCE; private String name; public void doSomeThing() { System.out.prin ...

  10. 深入设计模式(二)——单例模式(Singleton Pattern)

    一.单例模式介绍 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯一实例,它就可以严格地控制客户怎样访问它 ...

随机推荐

  1. 音视频八股文(7)-- 音频aac adts三层结构

    AAC介绍 AAC(Advanced Audio Coding)是一种现代的音频编码技术,用于数字音频的传输和存储领域.AAC是MPEG-2和MPEG-4标准中的一部分,可提供更高质量的音频数据,并且 ...

  2. 2023-03-03:请用go语言调用ffmpeg,摄像头捕获并编码为h264文件,不管音频。

    2023-03-03:请用go语言调用ffmpeg,摄像头捕获并编码为h264文件,不管音频. 答案2023-03-03: 使用 github.com/moonfdd/ffmpeg-go 库. 先用如 ...

  3. 2021-01-11:linux中,如何看内存的使用情况呢?

    福哥答案2021-01-11: 1.free:查看内存占用情况,会直接返回,常用参数 -M.-G 是以MB或GB为单位返回结果.2.sar:定时检测系统资源占用情况,-r 参数是内存资源,一般用法 s ...

  4. vue自定义组件——search-box

    pre { overflow-y: auto; max-height: 300px } github地址: https://github.com/lxmghct/my-vue-components 组 ...

  5. vue 中render执行流程梳理

    用了多年vue 今天对自己了解的render 做一个梳理 一.使用template模板 先从vue 初始化开始: 众所周知项目的main.js中定义了 var app = new Vue({})这vu ...

  6. js通过className删除元素

    有时候难免需要使用js进行 dom 操作:如在删除地图feature时同时得清除提示框 这个就需要使用 .parentNode.removeChild(元素) let chArr = document ...

  7. 效率神器!神级ChatGPT浏览器插件分享

    大家好,我是卷了又没卷,薛定谔的卷的AI算法工程师「陈城南」~ 担任某大厂的算法工程师,带来最新的前沿AI知识和工具,欢迎大家交流~,后续我还会分享更多 AI 有趣工具和实用玩法,包括AI相关技术.C ...

  8. Git使用教程(带你玩转GitHub)

    Git使用教程(理论实体结合体系版) 下载安装: 按照这个博客来就好 Windows系统Git安装教程(详解Git安装过程) - 学为所用 - 博客园 (cnblogs.com) Git命令大全: G ...

  9. Python潮流周刊#8:Python 3.13 计划将解释器提速 50%!

    你好,我是猫哥.这里每周分享优质的 Python 及通用技术内容,部分为英文,已在小标题注明.(标题取自其中一则分享,不代表全部内容都是该主题,特此声明.) 首发于我的博客:https://pytho ...

  10. Kafka中的消费者Offset

    消费者位移 每个 consumer 实例都会为它消费的分区维护属于自己的位置信息来记录当前消费了多少条消息.这在 Kafka 中有一个特有的术语:位移(offset). 相比较将offset保存在服务 ...