单例模式详解(附常见的7种单例模式源码)

article/2025/9/19 2:44:52

单例模式(Singleton Pattern):保证一个类仅有一个对象,并提供一个访问它的全局访问点。(Ensure a class only has one instance,and provide a globe point of access to it.)

常见应用场景:

  1. Windows的Task Manager(任务管理器)就是很典型的单例模式
  2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
  3. 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。
  4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作否则内容不好追加。
  6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
  7. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
  8. Application 也是单例的典型应用(Servlet编程中会涉及到)
  9. 在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理
  10. 在servlet编程中,每个Servlet也是单例
  11. 在spring MVC框架/struts1框架中,控制器对象也是单例
  12. 一个产品注册了一个商标,那么它就是单例的

单例模式的优点:

  1. 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动 时直接产生一个单例对象,然后永久驻留内存的方式来解决
  2. 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计 一个单例类,负责所有数据表的映射处理

常见单例模式有以下7种:

1.饿汉式:先创建后使用,线程安全,占用内存。代码如下:

/*** 饿汉式单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public class ClassA {//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象private ClassA(){ }//2.在类的内部创建一个类的实例//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!private static final ClassA instance = new ClassA();//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用//方法没有同步,调用效率高!public static ClassA  getInstance(){return instance;}//测试public static void main(String[] args) {ClassA a = ClassA.getInstance();ClassA b = ClassA.getInstance();System.out.println(a==b);}
}

控制台输出的结果如下图:
在这里插入图片描述
2.懒汉式:用的时候才创建,线程不安全,加锁会影响效率。资源利用率高了,但是,每次调用getInstance()方法都要同步,并发效率较低。代码如下:

/*** 懒汉式单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public class ClassB {//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象private ClassB(){ }//2.在类的内部创建一个类的实例private static ClassB instance ;//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用public static synchronized ClassB  getInstance(){if(instance == null) {instance = new ClassB();}return instance;}//测试public static void main(String[] args) {ClassB a = ClassB.getInstance();ClassB b = ClassB.getInstance();System.out.println(a==b);}
}

控制台输出的结果如下图:
在这里插入图片描述
3.静态内部类方式:也即饿汉式和懒汉式的组合,调用getInstance()方法时才创建,达到了类似懒汉式的效果,同时又是线程安全的。代码如下:

/*** 使用静态内部类方式实现单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public class ClassC {//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象private ClassC(){ }//2.在类的内部创建一个类的实例private static class Holder{private static ClassC instance = new ClassC();}//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用public static ClassC  getInstance(){return Holder.instance;}//测试public static void main(String[] args) {ClassC a = ClassC.getInstance();ClassC b = ClassC.getInstance();System.out.println(a==b);}
}

控制台输出的结果如下图:
在这里插入图片描述
4.枚举方法:线程安全,实现简单,调用效率高,不能延时加载。枚举本身就是单例模式,由JVM从根本上提供保障并且可以天然的防止反射和反序列化漏洞!需要继承的场景它就不适用了。枚举方式是Effective Java作者提倡的方式。代码如下:

/*** 使用枚举方法实现单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public enum ClassD {//定义一个枚举的元素,它就代表了Singleton的一个实例。INSTANCE;//对外部提供调用方法:将创建的对象返回,只能通过类来调用public void otherMethod(){//功能处理}//测试public static void main(String[] args) {ClassD a = ClassD.INSTANCE;ClassD b = ClassD.INSTANCE;System.out.println(a==b);}
}

5.双重校验锁式:通常线程安全,加volatile的作用是禁止指令重排。(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)代码如下:

/*** 使用双重校验锁来实现单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public class ClassE {//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象private ClassE(){ }//2.在类的内部创建一个类的实例private volatile static ClassE instance; //volatile作用:保证多线程可以正确处理instance//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用public static ClassE  getInstance(){if(instance == null){ //检查实例,如果为空,就进入同步代码块synchronized (ClassE.class){if(instance == null){ //再检查一次,仍未空才创建实例instance = new ClassE();}}}return instance;}//测试public static void main(String[] args) {ClassE a = ClassE.getInstance();ClassE b = ClassE.getInstance();System.out.println(a==b);}
}

控制台输出的结果如下图:
在这里插入图片描述

6.使用ThreadLocal实现:线程安全,ThreadLocal采用以空间换时间的方式,为每一个线程都提供一份变量,因此可以同时访问而互不影响。代码如下:

/*** 使用ThreadLocal实现单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public class ClassF {//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象private ClassF(){ }//2.在类的内部创建一个类的实例private static final ThreadLocal<ClassF> tls = new ThreadLocal<ClassF>(){@Overrideprotected ClassF initialValue(){return new ClassF();}};//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用public static ClassF  getInstance(){return tls.get();}//测试public static void main(String[] args) {ClassF a = ClassF.getInstance();ClassF b = ClassF.getInstance();System.out.println(a==b);}
}

控制台输出的结果如下图:
在这里插入图片描述
7.使用CAS锁来实现:(CAS锁(Compare and Swap):比较并交换,是一种有名的无锁算法,属于乐观锁)。用CAS锁来实现单例模式是线程安全的,代码如下:

/*** 使用CAS锁来实现单例模式* @author cui_yonghua  https://blog.csdn.net/cui_yonghua/article/details/90512943*/
public class ClassG {//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象private ClassG(){ }//2.在类的内部创建一个类的实例private static final AtomicReference<ClassG> instance = new AtomicReference<ClassG>(); //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用public static final ClassG getInstance(){for(;;){ClassG current = instance.get();if(current != null){return current;}current = new ClassG();if(instance.compareAndSet(null,current)){return current;}}}//测试public static void main(String[] args) {ClassG a = ClassG.getInstance();ClassG b = ClassG.getInstance();System.out.println(a==b);}
}

控制台输出的结果如下图:
在这里插入图片描述
如果如果想了解更多设计模式,可点击:设计模式概述 以及 23种设计模式的介绍

如果觉得文章写的不错,也可以小小地打赏一下嘛~ 也期待合作,“码”上改变~

在这里插入图片描述


http://chatgpt.dhexx.cn/article/pRB41t4q.shtml

相关文章

设计模式(一)—单例模式(附Java代码)

单例模式&#xff08;Singleton Pattern&#xff09;:采取一定的方法保证在整个的软件系统中&#xff0c;对于某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其实例对象的方法。 比如Hibernate的SessionFactory&#xff0c;它充当数据存储源的代理&#xff0c;…

线程的运行状态

不管是多线程还是多进程&#xff0c;实际上都不太可能一直占用CPU资源&#xff0c;所有多线程的几种状态一定要掌握。 多线程的状态如下图&#xff1a; 所有的系统费资源是有限的&#xff0c;不管是多线程还是多进程都必须在执行一段时间后让出资源&#xff0c;交由其他的线程…

一条SQL语句统计总数及各状态数

需求&#xff1a;共有协议X份&#xff0c;已签XX份&#xff0c;待签X份 sql: select count(1) 总记录数,sum(case when XY_STATUS1 then 1 else 0 end)待签,sum(case when XY_STATUS2 then 1 else 0 end)已签 from YG.T_ZHGL 结果&#xff1a; count(1):所有数据&#xff…

线程的执行状态

1,创建&#xff1a; 当创建好线程对象的时候&#xff0c;也就是new Thread类或者是new Thread子类的时候。此时称为创建状态 2&#xff0c;就绪&#xff1a; 当线程对象调用了start&#xff08;&#xff09;方法&#xff0c;开启线程了的时候&#xff0c;此时的线程已经开启了&…

Java线程线程的状态

1、线程的状态 线程有六种状态&#xff1a;分别如下 ① NEW(新建) 线程刚被创建&#xff0c;但是并未启动。还没调用start方法 ② Runnable(可运行) 线程可以在java虚拟机中运行的状态&#xff0c;可能正在运行自己代码&#xff0c;也可能没有&#xff0c;这取决于操作系统…

Java多线程批量执行sql

当遇到大sql批量导入时几十万上百万数据&#xff0c;使用plsql执行等都是非常的慢。因此开发一套自定义线程池处理sql&#xff1a; 1&#xff0c;线程代码 import java.util.ArrayList;/*** ClassName: com.ai.order.esb.yulang.tools.handle* Description: TODO* version: v1…

一条SQL语句是如何执行的?

大家六一儿童节好呀&#xff01; 接下来的一段时间内&#xff0c;将带领大家一同探索MySQL的奥妙&#xff0c;加油吧&#xff01;我们。 下面进入正题&#xff1a;一条SQL语句是如何进行的&#xff1f; 对于这个问题&#xff0c;我想将其分为两个问题来回答&#xff0c;分别是…

mysql 查看线程状态

show full PROCESSLIST 打开两个查询窗口&#xff0c;在A窗口执行一个查询时间较长的sql&#xff0c;在B窗口使用show full PROCESSLIST&#xff0c;可以看到A中执行的sql时间。 sleep表示没有操作&#xff0c;query表示正在查询。

线程状态总结

目录 文章目录 前言 一、线程状态图解 二、线程的几种状态 及线程过程 1.线程的几种状态 2.线程过程 总结 前言 总结了在华清远见这段时间所学的线程相关的知识点&#xff0c;文章记录了线程的几种状态及线程的过程 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案…

线程状态

原文&#xff1a;https://mp.weixin.qq.com/s/GsxeFM7QWuR--Kbpb7At2w 人类为了利用好自己的时间&#xff0c;经常会同时做多件事情&#xff0c;比如上厕所时刷手机&#xff0c;开车时听新闻... 对于自己尚且如此&#xff0c;对计算机也不能闲着。为了最大化的提升机器利用率&…

一条SQL语句的执行过程

摘要 本文站在后端开发的角度&#xff0c;讲述一条SQL从客户端发送到MySQL服务器进行处理&#xff0c;并将结果返回给客户端的过程。这个过程中涉及的操作会在后面的文章中做详细的分析。 连接建立 我们通常使用ORM框架来生成SQL语句&#xff0c;在发送SQL语句给MySQL服务器…

多线程执行sql报错处理

pymysql多线程访问数据库报错&#xff1a;Packet sequence number wrong - got 7 expected 2 原文&#xff1a;https://www.cnblogs.com/heiao10duan/p/9373237.html参考&#xff1a;https://www.jianshu.com/p/60c8e0e440ea原因&#xff1a; 使用了多线程&#xff0c;多线程共…

MySQL - 一条 SQL 语句是如何执行的(SQL执行详解)

前言 天天和数据库打交道&#xff0c;一天能写上几十条 SQL 语句&#xff0c;但你知道我们的系统是如何和数据库交互的吗&#xff1f;MySQL 如何帮我们存储数据、又是如何帮我们管理事务&#xff1f;....是不是感觉真的除了写几个 「select * from dual」外基本脑子一片空白&a…

一、SQL语句执行过程

一、MySQL架构图 MySQL逻辑架构图&#xff0c;可以分为 Server 层和存储引擎层两部分。 Server 层包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学和加密函数等&…

线程状态详解

线程的生命周期 new的时候即为创建状态。 调用start即为启动了&#xff0c;启动线程后就变为就绪状态&#xff0c; 就绪之后等待CPU的调度&#xff0c;CPU调度完之后&#xff0c;就进入了运行状态。 运行状态运行sleep方法时会进入阻塞状态&#xff0c;进入阻塞状态有非常多…

一条 SQL 语句是如何执行的

1. select 语句执行过程 一条 select 语句的执行过程如上图所示 1、建立连接 连接器会校验你输入的用户名和密码是否正确&#xff0c;如果错误会返回提示&#xff0c;如果正确&#xff0c;连接器会查询当前用户对于的权限。连接器的作用就是校验用户权限 2、查询缓存 MySQL…

SQL线程状态分析:processlist

老哥哔哔叨 我们已经写了很多 MySQL 的文章了&#xff0c;比如索引优化、数据库锁、主从复制等等。今天在来和大家学习一个优化方法&#xff1a;show processlist——查看当前所有数据库连接的 session 状态。帮助我们查看每个 SQL 线程的运行状态&#xff0c;是运行正常呀&…

线程的状态

1、线程有5种状态&#xff1a;新建&#xff08;new Thread&#xff09;、就绪&#xff08;runnable&#xff09;&#xff0c;运行&#xff08;running&#xff09;、阻塞&#xff08;blocked&#xff09;、结束&#xff08;dead&#xff09; 主要方法&#xff1a; setPriorit…

基于TCP和HTTP协议的RPC简单实现

一、RPC基本概念 1、基本概念 &#xff08;1&#xff09;RPC&#xff08;Remote Procedure Call Protocol&#xff09;——远程过程调用协议&#xff0c;它是一种通过网络从远程计算机程序上请求服务&#xff0c;而不需要了解底层网络技术的协议&#xff1b; &#xff08;2&…

RPC协议及常用框架

https://www.jianshu.com/p/8ba4b7b834aa RPC协议 RPC:远程过程调用&#xff0c;原则上来说系统间跨进程的调用都属于RPC范畴 RMI/HTTP/dubbo/Spring Cloud/thrift RPC框架如何实现分布式环境下的远程调用 在一个典型的RPC的使用场景中&#xff0c;包含了服务发现&#xf…