单例模式的几种实现方式

单例模式的实现有多种方式,如下所示:

1、懒汉式,线程不安全

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

 1 实例
2 public class Singleton {
3 private static Singleton instance;
4 private Singleton (){}
5
6 public static Singleton getInstance() {
7 if (instance == null) {
8 instance = new Singleton();
9 }
10 return instance;
11 }
12 }

接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。

2、懒汉式,线程安全

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

 1 实例
2 public class Singleton {
3 private static Singleton instance;
4 private Singleton (){}
5 public static synchronized Singleton getInstance() {
6 if (instance == null) {
7 instance = new Singleton();
8 }
9 return instance;
10 }
11 }

3、饿汉式

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

1 实例
2 public class Singleton {
3 private static Singleton instance = new Singleton();
4 private Singleton (){}
5 public static Singleton getInstance() {
6 return instance;
7 }
8 }

4、双检锁/双重校验锁(DCL,即 double-checked locking)

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程安全:

实现难度:较复杂

描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

 1 实例
2 public class Singleton {
3 private volatile static Singleton singleton;
4 private Singleton (){}
5 public static Singleton getSingleton() {
6 if (singleton == null) {
7 synchronized (Singleton.class) {
8 if (singleton == null) {
9 singleton = new Singleton();
10 }
11 }
12 }
13 return singleton;
14 }
15 }

5、登记式/静态内部类

是否 Lazy 初始化:

是否多线程安全:

实现难度:一般

描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

 1 实例
2 public class Singleton {
3 private static class SingletonHolder {
4 private static final Singleton INSTANCE = new Singleton();
5 }
6 private Singleton (){}
7 public static final Singleton getInstance() {
8 return SingletonHolder.INSTANCE;
9 }
10 }

6、枚举

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

1 实例
2 public enum Singleton {
3 INSTANCE;
4 public void whateverMethod() {
5 }
6 }

一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

Java 设计模式----单例模式的几种实现方式的更多相关文章

  1. JAVA设计模式---单例模式的几种实现方式比较

    1.延迟实例化方式:(懒汉模式) public class Singleton { private static Singleton uniqueInstance; private Singleton ...

  2. JAVA中单例模式的几种实现方式

    1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...

  3. Java设计模式 - 单例模式 (懒汉方式和饿汉方式)

    概念: Java中单例模式是一种常见的设计模式,单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的写法有好几种,这 ...

  4. java设计模式单例模式 ----懒汉式与饿汉式的区别

    常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...

  5. Java设计模式の单例模式

    -------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...

  6. JAVA设计模式-单例模式(Singleton)线程安全与效率

    一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...

  7. Java设计模式 - - 单例模式 装饰者模式

    Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...

  8. 【设计模式】Java设计模式 - 单例模式

    [设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...

  9. JAVA设计模式——单例模式

    单例模式的定义: Ensure a class has only one instance, and provide a global point of access to it.( 确保某一个类只有 ...

  10. JAVA实现单例模式的四种方法和一些特点

    JAVA实现单例模式的四种方法和一些特点,需要的朋友可以参考一下     一.饿汉式单例类 复制代码 代码如下: public class Singleton  {      private Sing ...

随机推荐

  1. 手写promise自定义封装异步任务回调的执行

    自定义封装异步任务回调的执行 <script type="text/javascript"> let p = new Promise((resolve, reject) ...

  2. hover时行级元素变成了块级元素,导致位置错乱

    在hover时,i元素变成了块级元素: 导致这两个元素各自占了一行: 最终导致样式错乱: <div class="demo"> <!-- 添加图标 和 编辑图标 ...

  3. docker 镜像导出和导入(适用于内网无法拉镜像的问题)

    1.在外网将镜像从指定的仓库拉下来 docker pull consul 现在已将consul镜像拉到了可连外网的服务器  2.将镜像把包到指定的tar文件中 docker save consul:l ...

  4. 关于Curl命令的使用

    最常用的curl命令 1.发送GET请求 curl URL 例: curl URL?a=1&b=nihao 2.发送POST请求 curl -X POST -d 'a=1&b=niha ...

  5. 在数据增强、蒸馏剪枝下ERNIE3.0分类模型性能提升

    在数据增强.蒸馏剪枝下ERNIE3.0模型性能提升 项目链接: https://aistudio.baidu.com/aistudio/projectdetail/4436131?contributi ...

  6. 【可观测性系列】 Opentelemetry 介绍

    作者简介:大家好,我是蓝胖子 ️博客首页:博客园主页蓝胖子的编程梦 每日一句:人生的烦恼,多在于明白的太多,而做的太少 大家好,我是蓝胖子,随着微服务的流行,服务的可观测性概念被越来越多人提及到,究竟 ...

  7. NC16513 无关(relationship)

    题目链接 题目 题目描述 若一个集合A内所有的元素都不是正整数N的因数,则称N与集合A无关. 给出一个含有k个元素的集合A={a1,a2,a3,...,ak},求区间[L,R]内与A无关的正整数的个数 ...

  8. NC14701 取数游戏2

    题目链接 题目 题目描述 给定两个长度为n的整数列A和B,每次你可以从A数列的左端或右端取走一个数.假设第i次取走的数为ax,则第i次取走的数的价值vi=bi⋅ax,现在希望你求出∑vi的最大值. 输 ...

  9. USB至串口TTL转接设备及Console线

    USB转串口常见芯片方案 FT232, FTDI(英国) 公认稳定可靠, 传输速率3Mbps, 功能最强, 单芯片内置SPI,TWI,JTAG,GPIO等功能. FT232BM为较早型号, FT232 ...

  10. 端口碰撞Port Knocking和单数据包授权SPA

    端口碰撞技术 Port knocking 从网络安全的角度,服务器开启的端口越多就越不安全,因此系统安全加固服务中最常用的方式,就是先关闭无用端口,再对提供服务的端口做访问控制.而作为远程管理与维护的 ...