【面试系列】6种单例模式(Singleton)实现方法比较
转载文章,文章经 LiteCodes 授权,转载至本博客。
原文地址:【面试系列】6种单例模式(Singleton)实现方法比较
下述代码均省略了 Singleton 类的业务代码段,仅表现作为单例所需的代码部分。
懒汉式
普通懒汉式
1 public class Singleton { 2 3 /** 单例对象 */ 4 private static Singleton instance; 5 6 /** 7 * 私有构造方法. 8 */ 9 private Singleton() { 10 } 11 12 /** 13 * 静态方法, 用于获取单利对象. 14 * 如果单例对象未创建, 则创建新单例对象, 否则直接返回该对象. 15 * 16 * @return 单例对象. 17 */ 18 public static Singleton getInstance() { 19 if (instance == null) { 20 instance = new Singleton(); 21 } 22 return instance; 23 } 24 }
最简单的懒汉式单例,在首次调用 getInstance(); 时,会对单例对象进行实例化。
然而,这种方式明显无法在多线程模式下正常工作。当线程并发调用getInstance(); 时,由于线程之间没有进行同步,有可能两个线程同时进入 if 条件,导致实例化两次。
线程安全的懒汉式
1 public class Singleton { 2 3 /** 单例对象 */ 4 private static Singleton instance; 5 6 /** 7 * 私有构造方法. 8 */ 9 private Singleton() { 10 } 11 12 /** 13 * 静态方法, 用于获取单利对象. 14 * 如果单例对象未创建, 则创建新单例对象, 否则直接返回该对象. 15 * 16 * @return 单例对象. 17 */ 18 public static synchronized Singleton getInstance() { 19 if (instance == null) { 20 instance = new Singleton(); 21 } 22 return instance; 23 } 24 }
最简单的线程安全的懒汉模式,通过在 getInstance() 方法上添加 synchronized 关键字,保证同一时间仅有一个线程能够执行该代码段,以保证不会出现上面一种方法产生的问题。
然而,这种方法效率很低。每次调用 getInstance() 方法,都将为代码段加锁,同一时间该代码段只能被一个线程访问。然而除了首次调用外,都是不需要同步的,因为 instance 已经被实例化。
Double-Check
1 public class Singleton { 2 3 /** 单例对象 */ 4 private static volatile Singleton instance; 5 6 /** 7 * 私有构造方法. 8 */ 9 private Singleton() { 10 } 11 12 /** 13 * 静态方法, 用于获取单利对象. 14 * 如果单例对象未创建, 则创建新单例对象, 否则直接返回该对象. 15 * 16 * @return 单例对象. 17 */ 18 public static Singleton getInstance() { 19 if (instance == null) { 20 synchronized (Singleton.class) { 21 if (instance == null) { 22 instance = new Singleton(); 23 } 24 } 25 } 26 return instance; 27 } 28 }
Double-check 即双重校验,该方法是针对上述方法提出的一种改进方案。
在 getInstance() 方法中,通过不加锁判断 instance 是否实例化。如果没有实例化,再进行加锁、实例化过程,以减少在实例化后调用 getInstance() 方法导致的性能损耗。
需注意的是,在代码第4行, instance 的定义处,添加了 volatile 关键字。
为了保证 instance 在多个线程间同步,需要通过 volatile 关键字,指明该变量的值每次需要从主存中直接获取,避免从线程内存中获取,以保证线程间同步。
饿汉式
1 public class Singleton { 2 3 /** 单例对象, 类装载时进行实例化. */ 4 private static final Singleton singleton = new Singleton(); 5 6 /** 7 * 私有构造方法. 8 */ 9 private Singleton() { 10 } 11 12 /** 13 * 静态方法, 用于获取单利对象. 14 * 15 * @return 单例对象. 16 */ 17 public static Singleton getInstance() { 18 return singleton; 19 } 20 }
饿汉式单例的原理是 ClassLoader 装载类是单线程,通过这种机制避免了线程同步问题。
这种方式虽然避免了线程同步问题,但却有可能带来性能问题。
无论该类是否被使用, ClassLoader 都有可能(也有可能被 ClassLoader 忽略)加载该类并实例化该单例对象。所以在基础类库场景下,这种方法会无故消耗更多的资源。
静态内部类方式
1 public class Singleton { 2 3 /** 4 * 私有构造方法. 5 */ 6 private Singleton() { 7 } 8 9 /** 10 * 静态方法, 用于获取单利对象. 11 * 12 * @return 单例对象. 13 */ 14 public static Singleton getInstance() { 15 return SingletonHolder.instance; 16 } 17 18 private static class SingletonHolder { 19 20 /** 单例对象, 类装载时进行实例化. */ 21 private static final Singleton instance = new Singleton(); 22 } 23 }
这种方法同样利用了 ClassLoader 单线程装载的方式,避免了线程同步问题。然而他和上面一种方法不同的地方在于, instance 对象只有在 SingletonHolder 类被装载的时候才会被实例化。也就是说,只有当 getInstance() 方法调用时,才会被实例化,这样就避免了上述的资源损耗。
枚举方式
1 public enum Singleton { 2 INSTANCE; 3 }
在《Effective Java》一书中,Joshua Bloch 推荐使用这种方式实现单例模式。这种方式不仅能够避免线程同步问题,而且由于其语法级的约束,JVM 级的支持,保证了其极强的正确性。
如何选择
俗话说,No silver bullet,每一种实现都有其适用的场景。那么,我们如何选择单例的实现方式呢?答案是:取决于你所期望的内容。
如果你的单例类应用频繁,从系统启动后就需要使用,那么,饿汉式可能是一个不错的选择。类加载过程便已经完成了实例化的单例,在之后的调用过程中,无需再进行实例化,也无需害怕因为线程同步导致的性能损耗。
如果你的单例类占用较多资源,并且调用频率较低,那么或许 Double-Check 的懒汉式是一个不错的选择。在单例使用前,并不会被实例化,其所需要的资源也并不会被占用。
如果你的单例类属于某一个类库,或许 Double-Check 的懒汉式是一个不错的选择。一个功能丰富的类库中,并非所有的类都会被使用。然而 ClassLoader 的加载机制,并不一定会将其排除至外。所以,一个懒汉式的单例有可能降低类库使用者的资源损耗。
……
根据你的应用场景,选择一个合适的单例模式吧~
转载于:https://www.cnblogs.com/rainisic/articles/six-ways-to-realize-singleton.html
【面试系列】6种单例模式(Singleton)实现方法比较相关推荐
- 程序员面试系列之Java单例模式的攻击与防御
我写的程序员面试系列 Java面试系列-webapp文件夹和WebContent文件夹的区别? 程序员面试系列:Spring MVC能响应HTTP请求的原因? Java程序员面试系列-什么是Java ...
- 极速理解设计模式系列:11.单例模式(Singleton Pattern)
单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 三要点: 一.单例类只能有一个实例 二.单例类必须自行创建自身实例 三.单例类自行向整个系统提供实例 类 ...
- Java高并发编程详解系列-7种单例模式
引言 在之前的文章中从技术以及源代码的层面上分析了关于Java高并发的解决方式.这篇博客主要介绍关于单例设计模式.关于单例设计模式大家应该不会陌生,作为GoF23中设计模式中最为基础的设计模式,实现起 ...
- 学习设计模式系列之一:单例模式
学习一下C++的单例模式(Singleton)实现方法,顺便练习一下多线程编程. 代码: 1 #include <stdlib.h> 2 #include <windows.h> ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- 异常数据4种剔除方法_数据分析系列 22/32 | 9种常用的数据分析方法
要使各种结构化的.非结构化的.海量的数据实现标准化.信息化,能够提供业务绩效评估.业务决策支持等要求,我们首先需要进行数据分析. 同时,围绕业务问题,采用合适的分析方法,分析模型,以及分析工具,这是数 ...
- 【Arduino和高中通用技术】——十一、BF1K-3AA系列电阻式压力应变片、HX711压力传感器和另一种按键去抖动方法
一.压力应变片 电阻式压力应变片比一些体重秤上使用的电压式压力应变片精度要高,但是成品的压力模块往往动辄几百元而且形态不能满足我们的各种"奇葩"应用.而这种裸片价格便宜,所以在最近 ...
- JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载
2019独角兽企业重金招聘Python工程师标准>>> 我写的程序员面试系列文章 Java面试系列-webapp文件夹和WebContent文件夹的区别? 程序员面试系列:Sprin ...
- 我向面试官讲解了单例模式,他对我竖起了大拇指
作者:小菠萝 单例模式相信大家都有所听闻,甚至也写过不少了,在面试中也是考得最多的其中一个设计模式,面试官常常会要求写出两种类型的单例模式并且解释其原理,废话不多说,我们开始学习如何很好地回答这一道面 ...
最新文章
- linux服务之drbd
- Photoshop简单另类方法给黑白照片上色
- 怎样才能让Android平板不卡,如何让你的安卓平板从获新生
- c++ template(8)模版多态
- (转)无特征过狗一句话猥琐思路
- Linux——SSH服务器
- django 视图模式
- jdk Collections类
- USB加密狗复制克隆软件
- java.lang.IllegalStateException: onMeasure() did not set the measured dimension by calling setMeasur
- shell 脚本获取时间戳
- KNN算法和kd树详解(例子+图示)
- C#毕业设计——基于C#+asp.net+SQL server的物料管理系统(ERP)设计与实现(毕业论文+程序源码)——物料管理系统(ERP)
- 组合导航(五):惯性导航参数建模
- CVR预估论文阅读笔记:一次性解决三大难题
- 编译Linux内核没有zImage,Linux 编译系统的简单介绍与内核编译安装
- GPU深度发掘 -- GPGPU数学基础教程
- JAVA商城项目(微服务框架)——第11天 elasticsearch搜索
- 打开idea后不显示界面
- 【待写】x265:CRF、ABR、CQP码控模式性能测试