单例模式是结构最简单的设计模式,用于创建软件系统中独一无二的对象;但如果深入研究,单例模式也是最复杂的设计模式

单例模式(Singleton Pattern):确保某一个类只有一个实例,且自行实例化,并向整个系统提供这个实例。单例模式是一种对象创建型模式

单例模式有两种不同的实现方式,饿汉式单例模式(Eager Singleton)懒汉式单例模式(Lazy Singleton)

相比于饿汉模式,懒汉模式要复杂的多

本文介绍懒汉式单例模式(Lazy Singleton),代码如下

package com.design.singleton;/*** 懒汉单例模式,线程不安全*/
public class LazySingleton {private static LazySingleton lazySingleton = null;private LazySingleton(){}public static LazySingleton getInstance(){if (lazySingleton == null){lazySingleton = new LazySingleton();}return lazySingleton;}
}

通过代码可以看出,懒汉单例模式在第一次调用 getInstance 方法时,实例化 LazySingleton 对象,在类加载时并不自行实例化,这种技术称为延迟加载技术(Lazy Load),即在需要的时候进行加载实例

仔细推敲上边代码,会发现它存在线程安全问题。即在多线程情况下,当线程1进入 if (lazySingleton == null) 的判断时,lazySingleton 对象还没有创建,因此 lazySingleton 对象为空;此时,线程2也进入 if (lazySingleton == null) 的判断,这时线程1和线程2将创建两个 lazySingleton 对象,这违背了单例模式的初衷 。那么如何解决懒汉模式线程安全的问题,请看下边代码

package com.design.singleton;/*** 懒汉单例模式*/
public class LazySingleton {private static LazySingleton lazySingleton = null;private LazySingleton(){}public synchronized static LazySingleton getInstance(){if (lazySingleton == null){lazySingleton = new LazySingleton();}return lazySingleton;}
}

通过在 getInstance() 方法前加 synchronized 关键字进行线程锁定,来解决多个线程同时访问的线程安全问题。但这种方式仍然不是最好的解决方式,因为每次调用 getInstance() 方法时,都需要进行线程锁定判断,在多线程高并发访问情况下,将会导致系统性能大幅度降低。如何继续改进代码,仔细分析会发现,无须对整个 getInstance() 方法进行锁定,只需要对创建实例的代码lazySingleton = new LazySingleton() 进行锁定即可,改进代码如下

package com.design.singleton;/*** 懒汉单例模式*/
public class LazySingleton {private static LazySingleton lazySingleton = null;private LazySingleton(){}public static LazySingleton getInstance(){if (lazySingleton == null){synchronized (LazySingleton.class){lazySingleton = new LazySingleton();}}return lazySingleton;}
}

看似解决了性能问题,但仔细推敲,会发现上边代码又存在线程安全问题

假设线程1和线程2都在调用 getInstance() 方法,此时 lazySingleton 对象为空,线程1和线程2都能通过 if 判断进入同步代码块,只是线程1进入 synchronized 锁定的代码中执行创建实例的代码时,线程2处于排队等待的状态,当线程1执行完成后,线程2并不知道实例已经创建完成,因此线程2将继续创建新的实例,这时将导致产生两个实例对象,违背了单例模式的设计思想,因此需要进一步改进代码。这时,我们使用双重检查锁定。

双重检查锁定(Double-Check Locking):在 synchronized 锁定的代码中再进行一次 lazySingleton == null 的判断,这样就可保证线程安全

使用双重检查锁定的懒汉单例模式代码如下

package com.design.singleton;/*** 双重检查懒汉单例模式* 保证线程安全*/
public class DoubleCheckLazySingleton {private volatile static DoubleCheckLazySingleton doubleCheckLazySingleton = null;private DoubleCheckLazySingleton(){}public static DoubleCheckLazySingleton getInstance(){if (doubleCheckLazySingleton == null){synchronized (DoubleCheckLazySingleton.class){if (doubleCheckLazySingleton == null){doubleCheckLazySingleton = new DoubleCheckLazySingleton();}}}return doubleCheckLazySingleton;}
}

注意,使用双重检查锁定的懒汉单例类,需要在静态成员变量 doubleCheckLazySingleton 前增加修饰符 volatile,增加 volatile修饰符的作用是保证多个线程都能够正确处理被 volatile 修饰的静态成员变量,即 volatile 禁止指令重排序,且保证了其修饰的静态成员变量 doubleCheckLazySingleton 的内存可见性,从而实现线程安全

双重检查锁定实现的单例模式并不是完美的实现方式,因为 volatile 关键字会屏蔽 java 虚拟机所做的一些优化代码,这可能导致系统运行效率降低。那么有没有更好的方式实现单例模式呢?答案是有的,即在单例类中增加一个静态内部类,并在静态内部类中创建单例对象,再通过静态方法 getInstance() 将单例对象返回给外部

静态内部类实现的单例模式代码如下

package com.design.singleton;/*** 静态内部类实现单例模式*/
public class StaticInnerClassSingleton {private StaticInnerClassSingleton(){}private static class StaticInnerClass{private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance(){return StaticInnerClass.staticInnerClassSingleton;}
}

通过代码可以看出,StaticInnerClassSingleton 类在类加载时不会实例化 StaticInnerClassSingleton 对象,而在第一次调用getInstance() 方法时,将加载静态内部类 StaticInnerClass,并初始化静态成员变量 staticInnerClassSingleton,由 java 虚拟机保证其线程安全,使得成员变量 staticInnerClassSingleton 只能初始化一次。且 getInstance() 方法没有线程锁定,因此不会影响性能

通过这种方式实现的懒汉单例模式,既实现了延迟加载,也保证了线程安全,且不影响系统性能

单例模式(懒汉模式)相关推荐

  1. 单例模式(懒汉模式-双检锁、饿汉模式、静态内部类模式)-详细

    文章目录 前言 单例模式(懒汉模式-双检锁.饿汉模式.静态内部类模式)-详细 01 单例模式是什么? 02 单例模式的好处? 03 单例模式的三种模式 03::01 懒汉模式 03::01::01 问 ...

  2. 单例模式---懒汉模式与饿汉模式

    单例模式:1)一个类只能创建一个实例2)构造函数和静态变量(加载类时即初始化)需为private3)get方法应该为public static,可供全局访问 //懒汉模式 public class l ...

  3. C++单例模式 : 懒汉模式 与 饿汉模式

     单例模式:         只能有一个实例,有懒汉和饿汉区分,实现核心思想:         1.构造函数私有化         2.使用静态函数作为接口来获取类对象 1.懒汉模式:         ...

  4. 大聪明教你学Java设计模式 | 第一篇:单例模式 (懒汉模式和饿汉模式)

    前言 大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案.大聪明本着"独乐乐不如众乐乐"的宗旨与大家分享一下设计模 ...

  5. 单例模式 之 单例模式——懒汉模式

    懒汉模式:和饿汉模式不同,懒汉模式并不会一开始声明对象,而是需要等到调用时再声明对象.他很懒,所以你叫"它"它才会动... 代码1: /*** 懒汉模式*/ public clas ...

  6. 单例模式——懒汉模式(C++)

    //懒汉模式 //优点: 第一次使用实例对象时,创建对象 //缺点: 复杂#include <iostream> #include <mutex> #include <t ...

  7. 设计模式——单例模式(懒汉模式,饿汉模式)

    声明: 本博客参考C语言中文网和优秀博客总结得出: (1)C语言中文网链接 (2)优秀博客链接 单例模式的定义: 指一个类只有一个实例,且该类能自行创建这个实例的一种模式.例如,Windows 中只能 ...

  8. 判断double_深入解析单例模式之懒汉模式---Double-Check及volatile关键字

    导读:在日常开发中对单例设计模式的应用十分常见,而看似简单小巧的设计模式其内部却蕴含着丰富的知识点.单例的创建方式有很多如懒汉模式和饿汉模式等.不同的语言又有不同的实现方式,但其本质的思想为:保证一个 ...

  9. Java多线程案例——单例模式(恶汉模式和懒汉模式)

    一,什么是单例模式 单例顾名思义指的是单个实例对象(所以单例模式要求构造方法私有化才能保证在类外不能创建该类的实例化对象):在有的场景中,不应该创建多个对象时就应该使用单例模式,一旦使用了单例模式,此 ...

  10. 单例模式的实现-懒汉模式【大话设计模式之爱你一万年:单例模式:我的女朋友只有你一个】

    2.单例模式的实现-懒汉模式 视频学习地址  -- <大话设计模式之爱你一万年>:https://dwz.cn/wqO0MAy7 这一节我们先来看看一种实现模式,懒汉模式,这也是这么中实现 ...

最新文章

  1. 修改值类型的实例方法 mutating
  2. redis live 如何安装
  3. SCA/SDO与WCF的比较
  4. 使用IE WebControls中的TabStrip控件和MultiPage控件实现选项卡式风格页面(转载)
  5. 一起手写Vue3核心模块源码,掌握阅读源码的正确方法
  6. 测试Spring Boot有条件的合理方式
  7. 内是不是半包围结构_轻钢别墅的体系结构
  8. 前端遇上Go: 静态资源增量更新的新实践
  9. 动手学CV-目标检测入门教程:基本概念
  10. 基于《悉尼协议》框架下Java课程案例教学研究
  11. 在计算机网络应用发展过程中 被称为,计算机网络技术与应用第三章考试题
  12. scrapy异步写入mysql_scrapy之异步写入数据库
  13. 【数据分享】糖尿病患者研究数据集
  14. python添加音乐_python给视频添加背景音乐并改变音量的具体方法
  15. Python数据可视化库汇总整理
  16. [jzoj NOIP2018模拟11.02]
  17. 现代优化算法 之 模拟退火
  18. “automation服务器不能创建对象”的问题的解决方案大全
  19. c语言的vcl库函数下载,VCL手册 PDF
  20. 软件测试应遵循哪些原则?

热门文章

  1. bt5重启网卡命令_BT5找不到无线网卡怎么处理
  2. 大数据审计的发展_关于发展大数据审计的几点思考
  3. PHP操控Excel视频教程
  4. (stream流)List转Map
  5. JavaScript全部替换 - js replaceAll()
  6. 切换IP软件,切换电脑手机IP如此简单
  7. 大华linux密码,大华ME-S-S系列双SD卡车载DVR
  8. Express访问静态资源(express.static)
  9. 简述u盘安装计算机系统的方法,电脑系统安装常见的两种方式(U盘)
  10. termux安装java,android下的termux模拟器安装openJDK和运行springboot项目