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

article/2025/9/19 2:44:51
单例模式(Singleton Pattern):采取一定的方法保证在整个的软件系统中,对于某个类只能存在一个对象实例,并且该类只提供一个取得其实例对象的方法。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够,这时就会使用到单例模式。

单例模式的八种方式:
  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举
单例模式使用场景和细节说明:
  • 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
  • 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
  • 单例模式使用的场景: 需要频繁的创建和销毁对象,创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。

1. 饿汉式(静态常量) 应用实例

  • 构造器私有化(防止 new)
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法

class Singleton1 {// 1. 构造器私有化 外部能newpublic Singleton1() {}// 2. 本类内部创建对象实例private final static Singleton1 instance = new Singleton1();// 3. 提供一个公有的静态方法,返回实例对象public static Singleton1 getInstance() {return instance;}
}
代码有缺点:
  • 优点: 写法简单,在类装载的时候就完成实例化。避免了线程同步问题。
  • 缺点: 在类加载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
  • 这总方式基于classloader机制规避了多线程的同步问题,不过 instance在类装载是就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式导致类装载,这时候初始化instance就没有达到lazy loading的效果。
  • 这种单例模式,可能会造成内存浪费。

2. 饿汉式(静态代码块)

class Singleton2 {// 1. 构造器私有化 外部能newpublic Singleton2() {}// 2. 本类内部创建对象实例private static Singleton2 instance;static { // 在静态代码块中,创建单例对象instance = new Singleton2();}// 3. 提供一个公有的静态方法,返回实例对象public static Singleton2 getInstance() {return instance;}
}
优缺点
  • 这种方式与静态常量方式类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执静态代码中的代码,初始化类的实例。 优缺点与静态常量写法相同。

3. 懒汉式(线程不安全)

  • 代码演示:
class Singleton3 {private static Singleton3 instance;private Singleton3() {}// 提供一个静态共有方法,当使用该方法时,才去创建instance, 即懒汉式public static Singleton3 getInstance() {if (null == instance) {instance = new Singleton3();}return instance;}}
优缺点:
  • 起到了Lazy Loading的效果,但只能在单线程下使用。
  • 如果在多线程下,一个线程进入了if(singleton==null) 判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
  • 结论: 在实际开发中,不要使用这种方式。

4. 线程安全懒汉式(同步方法)

  • 代码演示:
class Singleton4 {private static Singleton4 instance;private Singleton4() {}// 提供一个静态公有方法, 加入同步处理的代码, 解决线程安全问题public static synchronized Singleton4 getInstance() {if (null == instance) {instance = new Singleton4();}return instance;}
}
优缺点:
  • 解决了线程不安全问题
  • 效率太低,每个线程在想获取类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获取改类实例,直接return就行了。 方法同步效率太低。
  • 结论: 在实际开发中,不推荐使用这种方式。

5. 线程安全懒汉式(同步代码块)

  • 代码演示:
class Singleton5 {private static Singleton5 instance;private Singleton5() {}// 提供一个静态公有方法, 加入同步处理的代码, 解决线程安全问题public static Singleton5 getInstance() {if (null == instance) {synchronized (Singleton5.class){instance = new Singleton5();}}return instance;}
}
优缺点:
  • 对第四种方式的改进,前面同步方法效率太低。
  • 但是这总同步并不能起到线程同步的作用。跟第三种实现方式遇到的情形一致,加入一个线程进入判断语句,还未来得及往下执行,另一个线程也通过了这个判断语句。这是便会产生多个实例。

6. 双重检查

class Singleton6 {private static Singleton6 instance;private Singleton6() {}// 提供一个静态公有方法, 通过双if即双重检查锁的方式: synchronized中只有一个线程可以执行,第二个if中的语句只能被执行一次。// 解决线程安全和懒加载问题public static Singleton6 getInstance() {if (null == instance) {synchronized (Singleton6.class){if (null == instance) {instance = new Singleton6();}}}return instance;}
}
优缺点:
  • Double-check概念是多线程开发中常用到的,如代码所示:我们进行了两次if(null == singleton)检查,这样就可以保证线程安全了。
  • 这样,实例化代码只用执行一次,后面再次访问时,判断 if(null == singleton) 直接return实例化对象,也避免了反复进行方法同步。
  • 线程安全;延迟加载;效率较高。
  • 结论: 在实际开发中,推荐使用这种单例模式。

7. 静态内部类

class Singleton7 {private static Singleton7 instance;private Singleton7() {}// 写一个静态内部类,该类中有一个静态属性Singletonprivate static class SingletonInstance {private static final Singleton7 INSTANCE = new Singleton7();}// 提供一个静态的公有方法,直接返回SingletonInstance.INSTANCEpublic static synchronized Singleton7 getInstance(){return SingletonInstance.INSTANCE;}
}
优缺点:
  • 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
  • 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载Singleton类,从而完成Singleton的实例化。
  • 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
  • 优点: 避免了线程不安全,利用静态内部类特点实现低延迟加载,效率高。
  • 结论: 推荐使用。

8.枚举

enum Singleton8 {INSTANCE;
}
优缺点:
  • 这借助JDK1.5中添加的枚举来实现 单例模式。不仅能避免多线程同步问题,而且还能方式反序列化重新创建新的对象。
  • 这种方式是Effective Java做种Josh Bloch提倡的方式。
  • 结论: 推荐使用。

单例模式在JDK中的运用

  • java.lang.Runtime就是经典的单例模式
  • 在这里插入图片描述

http://chatgpt.dhexx.cn/article/58ljBocU.shtml

相关文章

线程的运行状态

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

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

需求:共有协议X份,已签XX份,待签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 结果: count(1):所有数据&#xff…

线程的执行状态

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

Java线程线程的状态

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

Java多线程批量执行sql

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

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

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

mysql 查看线程状态

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

线程状态总结

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

线程状态

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

一条SQL语句的执行过程

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

多线程执行sql报错处理

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

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

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

一、SQL语句执行过程

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

线程状态详解

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

一条 SQL 语句是如何执行的

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

SQL线程状态分析:processlist

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

线程的状态

1、线程有5种状态:新建(new Thread)、就绪(runnable),运行(running)、阻塞(blocked)、结束(dead) 主要方法: setPriorit…

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

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

RPC协议及常用框架

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

【RPC】RPC基础(二)RPC协议

文章目录 RPC核心原理1. RPC基础1.2 RPC协议为什么设计RPC协议如何设计RPC协议可扩展协议的设计思考 RPC核心原理 1. RPC基础 1.2 RPC协议 RPC协议和HTTP协议一样都属于应用层协议 协议的作用: ​ 协议就像是文章中的标点符号、段落格式等规定,有了…