前言

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

饿汉单例

是否多线程安全:是

是否懒加载:否

正如名字含义,饿汉需要直接创建实例。

public class EhSingleton {private static EhSingleton ehSingleton = new EhSingleton();private EhSingleton() {}public static EhSingleton getInstance(){return ehSingleton;}
}

缺点: 类加载就初始化,浪费内存
优点: 没有加锁,执行效率高。还是线程安全的实例。

懒汉单例

懒汉单例,在类初始化不会创建实例,只有被调用时才会创建实例。

非线程安全的懒汉单例

是否多线程安全:否

是否懒加载: 是

public class LazySingleton {private static LazySingleton ehSingleton;private LazySingleton() {}public static LazySingleton getInstance() {if (ehSingleton == null) {ehSingleton = new LazySingleton();}return ehSingleton;}}

实例在调用 getInstance 才会创建实例,这样的优点是不占内存,在单线程模式下,是安全的。但是多线程模式下,多个线程同时执行 if (ehSingleton == null) 结果都为 true,会创建多个实例,所以上面的懒汉单例是一个线程不安全的实例。

加同步锁的懒汉单例

是否多线程安全:是

是否懒加载: 是

为了解决多个线程同时执行 if (ehSingleton == null) 的问题,getInstance 方法添加同步锁,这样就保证了一个线程进入了 getInstance 方法,别的线程就无法进入该方法,只有执行完毕之后,其他线程才能进入该方法,同一时间只有一个线程才能进入该方法。

public class LazySingletonSync {private static LazySingletonSync lazySingletonSync;private LazySingletonSync() {}public static synchronized LazySingletonSync getInstance() {if (lazySingletonSync == null) {lazySingletonSync =new LazySingletonSync();}return lazySingletonSync;}}

这样配置虽然保证了线程的安全性,但是效率低,只有在第一次调用初始化之后,才需要同步,初始化之后都不需要进行同步。锁的粒度太大,影响了程序的执行效率。

双重检验懒汉单例

是否多线程安全:是

是否懒加载:是

使用 synchronized 声明的方法,在多个线程访问,比如A线程访问时,其他线程必须等待A线程执行完毕之后才能访问,大大的降低的程序的运行效率。这个时候使用 synchronized 代码块优化执行时间,减少锁的粒度

双重检验首先判断实例是否为空,然后使用 synchronized (LazySingletonDoubleCheck.class) 使用类锁,锁住整个类,执行完代码块的代码之后,新建了实例,其他代码都不走 if (lazySingletonDoubleCheck == null) 里面,只会在最开始的时候效率变慢。而 synchronized 里面还需要判断是因为可能同时有多个线程都执行到 synchronized (LazySingletonDoubleCheck.class) ,如果有一个线程线程新建实例,其他线程就能获取到 lazySingletonDoubleCheck 不为空,就不会再创建实例了。

public class LazySingletonDoubleCheck {private static LazySingletonDoubleCheck lazySingletonDoubleCheck;private LazySingletonDoubleCheck() {}public static LazySingletonDoubleCheck getInstance() {if (lazySingletonDoubleCheck == null) {synchronized (LazySingletonDoubleCheck.class) {if (lazySingletonDoubleCheck == null) {lazySingletonDoubleCheck = new LazySingletonDoubleCheck();}}}return lazySingletonDoubleCheck;}
}

静态内部类

是否多线程安全:是

是否懒加载:是

外部类加载时,并不会加载内部类,也就不会执行 new SingletonHolder(),这属于懒加载。只有第一次调用 getInstance() 方法时才会加载 SingletonHolder 类。而静态内部类是线程安全的。

静态内部类为什么是线程安全

静态内部类利用了类加载机制的初始化阶段 方法,静态内部类的静态变量赋值操作,实际就是一个 方法,当执行 getInstance() 方法时,虚拟机才会加载 SingletonHolder 静态内部类,

然后在加载静态内部类,该内部类有静态变量,JVM会改内部生成方法,然后在初始化执行方法 —— 即执行静态变量的赋值动作。

虚拟机会保证 方法在多线程环境下使用加锁同步,只会执行一次 方法。

这种方式不仅实现延迟加载,也保障线程安全。

public class StaticClass {private StaticClass() {}private static class SingletonHolder {private static final SingletonHolder INSTANCE = new SingletonHolder();}public static final SingletonHolder getInstance() {return SingletonHolder.INSTANCE;}
}

总结

  • 饿汉单例类加载就初始化,在没有加锁的情况下实现了线程安全,执行效率高。但是无论有没有调用实例都会被创建,比较浪费内存。
  • 为了解决内存的浪费,使用了懒汉单例,但是懒汉单例在多线程下会引发线程不安全的问题。
  • 不安全的懒汉单例,使用 synchronized 声明同步方法,获取实例就是安全了。
  • synchronized 声明方法每次线程调用方法,其它线程只能等待,降低了程序的运行效率。
  • 为了减少锁的粒度,使用 synchronized 代码块,因为只有少量的线程获取实例,实例是null,创建实例之后,后续的线程都能获取到线程,也就无需使用锁了。可能多个线程执行到 synchronized ,所以同步代码块还需要再次判断一次。
  • 静态内部类赋值实际是调用 方法,而虚拟机保证 方法使用锁,保证线程安全。

5种方式实现 Java 单例模式相关推荐

  1. java向MySQL插入当前时间的四种方式和java时间日期格式化的几种方法(案例说明)...

    转载地址:http://www.devba.com/index.php/archives/4581.html java向MySQL插入当前时间的四种方式和java时间日期格式化的几种方法(案例说明); ...

  2. java直接调用复制文件,java中文件复制的4种方式,java文件的复制

    java中文件复制的4种方式,java文件的复制 今天一个同事问我文件复制的问题,他一个100M的文件复制的指定目录下竟然成了1G多,吓我一跳,后来看了他的代码发现是自己通过字节流复制的,定义的字节数 ...

  3. mysql java 日期格式化_(转)java向MySQL插入当前时间的四种方式和java时间日期格式化的几种方法(案例说明)...

    java向MySQL插入当前时间的四种方式和java时间日期格式化的几种方法(案例说明);部分资料参考网络资源 1. java向MySQL插入当前时间的四种方式 第一种:将java.util.Date ...

  4. java加载properties文件的几种方式,java高级面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...

  5. java excel生成_两种方式实现java生成Excel

    Web应用中难免会遇到需要将数据导出并生成excel文件的需求.同样,对于本博客中的总结,也是建立在为了完成这样的一个需求,才开始去了解其实现形式,并且顺利完成需求的开发,先将实现过程总结于此.本博文 ...

  6. 读取Java文件到byte数组的三种方式及Java文件操作大全(包括文件加密,String加密)

    读取Java文件到byte数组的三种方式 package zs;import java.io.BufferedInputStream; import java.io.ByteArrayOutputSt ...

  7. 【第21期】以实际项目作驱动,换种方式学Java

    Java作为OOP(面向对象编程)的集大成者,融合了其他语言的诸多优点,让开发者体验到了函数式编程的精妙及强大之处. 随着这几年开源社区的蓬勃发展,越来越多的组件.框架.方案如雨后春笋般涌现,现代工程 ...

  8. pdf转图片的两种方式(java)

        最近在工作中遇到业务需要把pdf转成图片供前台预览,在网上查了一些方法,有的能用,有的缺东西,时间又比较急,没时间去看.在这里我整理了两种亲测可行的方式,节省大家时间带备忘: 1.pdf按页数 ...

  9. SpringBoot中使用AMQ的两种方式(Java配置、注解方式)

    Java配置方式使用AMQ 1. 引入依赖(pom.xml) <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns ...

  10. python网络通信的几种方式_两种方式,java=python,使用py4j进行通信

    我使用py4j实现python和java之间的通信,能够从java端调用python方法.但从python我不能发送任何对象或调用java方法.这是我试过的密码.在 我的java代码:public i ...

最新文章

  1. 联想拯救者y7000怎么配置Java环境_联想拯救者y7000重装系统教程
  2. 轻松搞定Retrofit不同网络请求方式的请求参数配置,及常用注解使用
  3. mysql 新增更新_MySQL新增数据,存在就更新,不存在就添加(转帖加实测)
  4. python打开摄像头获取图片_Python基于opencv调用摄像头获取个人图片的实现方法
  5. python3 发送邮件
  6. OpenCV 从文件中读取视频并播放
  7. 对于c语言int类型和float,以及double类型表示范围的计算
  8. springboot整合redis操作缓存(将查询到的数据放在缓存中)
  9. AAAI 2018 论文 | 蚂蚁金服公开最新基于笔画的中文词向量算法
  10. async 与 await 的用法详解
  11. 中国数码相机镜头行业市场供需与战略研究报告
  12. Appium探索—Mac OS Python版
  13. 猴子排序的期望复杂度推导(雾)
  14. “牙医”教你用450行Go代码自制编程语言 | Gopher Daily (2021.01.15) ʕ◔ϖ◔ʔ
  15. dos2unix命令
  16. 静态代理和动态代理的区别,什么场景使用?
  17. 牛客网 - [牛客假日团队赛6]Charm Bracelet(01背包)
  18. 《人机交互技术》 第八章 移动界面设计
  19. linux dot命令,DOT语言使用笔记(1)
  20. 关于java文件乱码解决

热门文章

  1. centos7服务器安装无线网卡,CentOS 7 安装无线网卡驱动方法实例教程
  2. 局域网文件服务器带宽,高带宽局域网
  3. 镜像css3,CSS下镜像翻转(水平/垂直翻转)
  4. TcaplusDB X 光与夜之恋,来谈一场高沉浸式的恋爱吧
  5. Inno Setup 6.0.3+ 简体中文语言包
  6. 如何Altium Designer中输出元件清单(BOM表格)
  7. 易语言删除c盘源码,易语言基础教程利用API删除自身及子文件
  8. 深度Linux安装红警2,深度Deepin Linux v20 Beta下玩红警
  9. windows11无法添加朝鲜语输入法
  10. python谐音梗_谐 音 梗 生 成 器