文章目录

  • 1. 单例模式的定义
  • 2. 单例模式的设计要素
  • 3. 单例模式的6种实现及各实现的优缺点
    • 3.1 懒汉式(线程不安全)
    • 3.2 饿汉式(线程安全)
    • 3.3 懒汉式(线程安全)
    • 3.4 双重检查锁实现(线程安全)
    • 3.5 静态内部类实现(线程安全)
    • 3.6 枚举类实现(线程安全)
  • 4. 单例模式的应用场景

1. 单例模式的定义

定义: 确保一个类只有一个实例,并提供该实例的全局访问点。这样做的好处是:有些实例,全局只需要一个就够了,使用单例模式就可以避免一个全局使用的类,频繁的创建与销毁,耗费系统资源。

2. 单例模式的设计要素

  • 一个私有构造函数 (确保只能单例类自己创建实例)
  • 一个私有静态变量 (确保只有一个实例)
  • 一个公有静态函数 (给使用者提供调用方法)

​ 简单来说就是,单例类的构造方法不让其他人修改和使用;并且单例类自己只创建一个实例,这个实例,其他人也无法修改和直接使用;然后单例类提供一个调用方法,想用这个实例,只能调用这个对外暴露的方法。这样就确保了全局只创建了一次实例。

3. 单例模式的6种实现及各实现的优缺点

3.1 懒汉式(线程不安全)

实现:

public class Singleton {private static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}
}

说明: 先不创建实例,当第一次被调用时,再创建实例,所以被称为懒汉式。

优点: 延迟了实例化,如果不需要使用该类,就不会被实例化,节约了系统资源。

缺点: 线程不安全,多线程环境下,如果多个线程同时进入了 if (uniqueInstance == null) ,若此时还未实例化,也就是uniqueInstance == null,那么就会有多个线程执行 uniqueInstance = new Singleton(); 就会实例化多个实例。

3.2 饿汉式(线程安全)

实现:

public class Singleton {private static Singleton uniqueInstance = new Singleton();private Singleton() {}public static Singleton getUniqueInstance() {return uniqueInstance;}}

说明: 先不管需不需要使用这个实例,直接先实例化好实例 (饿死鬼一样,所以称为饿汉式),然后当需要使用的时候,直接调方法就可以使用了。

优点: 提前实例化好了一个实例,避免了线程不安全问题的出现。

缺点: 直接实例化好了实例,不再延迟实例化;若系统没有使用这个实例,或者系统运行很久之后才需要使用这个实例,会造成操作系统的资源浪费。

3.3 懒汉式(线程安全)

实现:

public class Singleton {private static Singleton uniqueInstance;private static singleton() {}private static synchronized Singleton getUinqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}}

说明: 实现和 线程不安全的懒汉式 几乎一样,唯一不同的点是,在get方法上 加了一把锁。如此一来,多个线程访问,每次只有拿到锁的的线程能够进入该方法,避免了多线程不安全问题的出现。

优点: 延迟实例化,节约了资源,并且是线程安全的。

缺点: 虽然解决了线程安全问题,但是性能降低了。因为,即使实例已经实例化了,既后续不会再出现线程安全问题了,但是锁还在,每次还是只能拿到锁的线程进入该方法使线程阻塞,等待时间过长。

3.4 双重检查锁实现(线程安全)

实现:

public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}
}

说明: 双重检查数相当于是改进了 线程安全的懒汉式。线程安全的懒汉式 的缺点是性能降低了,造成的原因是因为即使实例已经实例化,依然每次都会有锁。而现在,我们将锁的位置变了,并且多加了一个检查。 也就是,先判断实例是否已经存在,若已经存在了,则不会执行判断方法内的有锁方法了。 而如果,还没有实例化的时候,多个线程进去了,也没有事,因为里面的方法有锁,只会让一个线程进入最内层方法并实例化实例。如此一来,最多最多,也就是第一次实例化的时候,会有线程阻塞的情况,后续便不会再有线程阻塞的问题。

为什么使用 volatile 关键字修饰了 uniqueInstance 实例变量 ?

​ uniqueInstance = new Singleton(); 这段代码执行时分为三步:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

​ 正常的执行顺序当然是 1>2>3 ,但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。
单线程环境时,指令重排并没有什么问题;多线程环境时,会导致有些线程可能会获取到还没初始化的实例。
例如:线程A 只执行了 1 和 3 ,此时线程B来调用 getUniqueInstance(),发现 uniqueInstance 不为空,便获取 uniqueInstance 实例,但是其实此时的 uniqueInstance 还没有初始化。

​ 解决办法就是加一个 volatile 关键字修饰 uniqueInstance ,volatile 会禁止 JVM 的指令重排,就可以保证多线程环境下的安全运行。

优点: 延迟实例化,节约了资源;线程安全;并且相对于 线程安全的懒汉式,性能提高了。

缺点: volatile 关键字,对性能也有一些影响。

3.5 静态内部类实现(线程安全)

实现:

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

说明: 首先,当外部类 Singleton 被加载时,静态内部类 SingletonHolder 并没有被加载进内存。当调用 getUniqueInstance() 方法时,会运行 return SingletonHolder.INSTANCE; ,触发了 SingletonHolder.INSTANCE ,此时静态内部类 SingletonHolder 才会被加载进内存,并且初始化 INSTANCE 实例,而且 JVM 会确保 INSTANCE 只被实例化一次。

优点: 延迟实例化,节约了资源;且线程安全;性能也提高了。

3.6 枚举类实现(线程安全)

实现:

public enum Singleton {INSTANCE;//添加自己需要的操作public void doSomeThing() {}}

说明: 默认枚举实例的创建就是线程安全的,且在任何情况下都是单例。

优点: 写法简单,线程安全,天然防止反射和反序列化调用。

防止反序列化
​ **序列化:**把java对象转换为字节序列的过程;
反序列化: 通过这些字节序列在内存中新建java对象的过程;
说明: 反序列化 将一个单例实例对象写到磁盘再读回来,从而获得了一个新的实例。我们要防止反序列化,避免得到多个实例。
枚举类天然防止反序列化。
其他单例模式 可以通过 重写 readResolve() 方法,从而防止反序列化,使实例唯一重写 readResolve() :

private Object readResolve() throws ObjectStreamException{return singleton;
}

4. 单例模式的应用场景

应用场景举例:

  • 网站计数器。
  • 应用程序的日志应用。
  • Web项目中的配置对象的读取。
  • 数据库连接池。
  • 多线程池。

使用场景总结:

  • 频繁实例化然后又销毁的对象,使用单例模式可以提高性能。
  • 经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏。
  • 使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信。

java单例模式与线程安全相关推荐

  1. java单例模式之线程安全问题

    单例的目的是为了保证运行时Singleton类只有唯一的一个实例,用于一些较大开销的操作. 饿汉式(没有线程安全问题): ' 由于使用static关键字进行了修饰,只能获取到一个对象,从而达到了单例, ...

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

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

  3. Java 单例模式探讨

    以下是我再次研究单例(Java 单例模式缺点)时在网上收集的资料,相信你们看完就对单例完全掌握了 Java单例模式应该是看起来以及用起来简单的一种设计模式,但是就实现方式以及原理来说,也并不浅显哦. ...

  4. Java单例模式优化写法

    转载自 http://blog.csdn.net/diweikang/article/details/51354982 Java单例模式优化写法 方法一:推荐 [java] view plain co ...

  5. Java单例模式的几种实现方式

    Java单例模式的几种实现方式 在Java 中,单例类只能有一个实例,必须创建自己的唯一实例,单例类必须给所有其他对象提供这一实例.Java 单例模式有很多种实现方式,在这里给大家介绍单例模式其中的几 ...

  6. Java 单例模式:懒加载(延迟加载)和即时加载

    Java 单例模式:懒加载(延迟加载)和即时加载 引言 在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制(或懒加载.延时加载),也就是说只有当使用到这个实例的时候才会创 ...

  7. Java单例模式:为什么我强烈推荐你用枚举来实现单例模式

    写在前面--原作的这篇文章真的写的非常的简洁,逻辑清晰,将Java单例模式的各种写法写的非常清楚,并介绍了用枚举实现单例的最佳实践. 单例模式简介 单例模式是 Java 中最简单,也是最基础,最常用的 ...

  8. 【深入】java 单例模式

    关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便萌生了写这篇文章的念头.企图 ...

  9. java单例模式 三种_三种java单例模式概述

    在java语言的应用程序中,一个类Class只有一个实例存在,这是由java单例模式实现的.Java单例模式是一种常用的软件设计模式,java单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种.下 ...

  10. 什么是java单例模式?

    关于java单例模式的文章早已是非常多了,本文是对我个人过往学习java,理解及应用java单例模式的一个总结.此文内容涉及java单例模式的基本概念,以及什单例模式的优缺点,希望对大家有所帮助. 什 ...

最新文章

  1. 一起谈.NET技术,OnLoad与Page_Load的差异分析
  2. Python--day28--set去重
  3. LoadRunner界面分析(二)
  4. 使用Spring Boot构建独立的OAuth服务器(三)
  5. dll 源码_软件源代码调试,你用过什么方法?看我介绍上位机源码调试
  6. 王石先生深奥的脑筋急转弯
  7. ASP.NET Core中的TagHelper及其用法
  8. 手势识别整体设计流程方案
  9. 禅与摩托车维修艺术摘录
  10. 功夫熊猫中英文字幕版好句子_了解熊猫中的带有复制警告的设置
  11. 【资源分享】如何画出论文中漂亮的图?
  12. 苹果微信点开才会收到信息_最新!苹果突然承认:iPhone12是问题产品
  13. 容器化技术(十一):Kubernetes中Pod的调度策略-标签与污点实战
  14. 跟我一起玩Win32开发
  15. 什么是Vue CLI(脚手架)?
  16. TypeScript类
  17. 使用数据挖掘软件Rapidminer进行关联规则分析
  18. Ice飞冰理解《一》
  19. 低功耗单片机系统的设计策略
  20. css ul li 图标 图片位置定位 背景定位

热门文章

  1. android 盒子刷机,刷机精灵 – 最好用的Android刷机工具
  2. BXP网卡换槽之后就要按“任意键”的问题解决方法!(转)
  3. 使用花生壳将内网服务发布到外网
  4. Linux花生壳使用篇
  5. CUDA编程入门教程
  6. 简谈触摸板程序的实现(一)
  7. 历经万难,终于搭好深度学习环境[吐血总结篇,造福后人]
  8. 21天学通C语言-学习笔记(11)
  9. 各类曲线的参数方程_曲线的参数方程
  10. SLAM方向国内有哪些优秀的公司?