【Java开发】设计模式 01:单例模式
1 单例模式介绍
单例模式(Singleton Pattern)是Java中最为基础的设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1.1 场景
目的:控制实例数目,节省系统资源;
适用:若一个全局使用的类频繁地创建与销毁,单例模式可以证一个类仅有一个实例,并提供一个访问它的全局访问点。
要求生产唯一序列号;
WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来;
创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
优点:
如此内存中只有一个实例,减少了内存的开销;
避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
注:spring boot项目中将该对象注入到bean,那么该对象就默认为单例,这时也可以使用@Scope去设定单例和原型以及其他模式。
1.2基础实现方式
以下是基础的实现方式,创建一个 Singleton 类,存在私有构造函数和本身的一个静态实例。
Singleton 类提供了一个静态方法,供外界获取它的静态实例。Client,我们的演示类使用 Singleton 类来获取 Singleton 对象。

📌 1.创建Singleton单例类
public class Singleton {//让构造函数为 private,这样该类就不会被实例化private Singleton(){}//创建 SingleObject 的一个对象private static Singleton INSTANCE = new Singleton();//获取唯一可用的对象public static Singleton getInstance(){return INSTANCE;}}📌 2.从singleton类获取唯一的对象。
public class Client {public static void main(String[] args) {//不合法的构造函数--因为SingleObject()私有,不可见
// Singleton singleton = new Singleton();Singleton singleton1 = Singleton.getInstance();Singleton singleton2 = Singleton.getInstance();System.out.println(singleton1);System.out.println(singleton2);}
}控制台输出:

2 实现方式汇总
类型 | Lazy 初始化 | 多线程安全 | 实现难度 |
懒汉式--线程不安全 | 是 | 否 | 容易 |
懒汉式--线程安全 | 是 | 是 | 容易 |
饿汉式 | 否 | 是 | 容易 |
双重校验锁 | 是 | 是 | 较复杂 |
双重校验锁+volatile | 是 | 是 | 较复杂 |
静态内部类 | 是 | 是 | 一般 |
枚举 | 否 | 是 | 容易 |
ThreadLocal | 是 | 是 | 较复杂 |
CAS锁 | 是 | 是 | 较复杂 |
2.1 懒汉式--线程不安全
这种方式是最基本的实现方式,最大的问题是不支持多线程,因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
public class Lazyman1 {private static Lazyman1 instance;private Lazyman1(){}public static Lazyman1 getInstance() {if (instance == null){instance = new Lazyman1();}return instance;}
}
接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。
2.2 懒汉式--线程安全
这种方式能够在多线程中很好的工作,但是效率低。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Lazyman2 {private static Lazyman2 instance;private Lazyman2(){}public static synchronized Lazyman2 getInstance() {if (instance == null){instance = new Lazyman2();}return instance;}
}2.3 饿汉式
这种方式比较常用,但容易产生垃圾对象。它基于类加载机制避免了多线程的同步问题,因为类加载过程JVM会自动加锁,因此保证了单例特性。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class Hungry {private Hungry(){}private final static Hungry HUNGRY = new Hungry();public static Hungry getInstance(){return HUNGRY;}
}2.4 双重校验锁(DCL,即 double-checked locking)
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
不过DCL还是可能会出现指令重排的问题~
public class DLC {private static DLC instance;private DLC(){}public static DLC getInstance() {if (instance==null){synchronized (LazyMan.class){if (instance==null){instance = new DLC();//不是一个原子性操作/** 1.分配内存空间* 2.执行构造方法,初始化对象* 3.把这个对象指向这个空间** 可能:* 123* 132 若多线程,A到达3,B会认为lazyMan非空,但是lazyMan此时还没有完成构造,那么就会有问题*/}}}return instance;}
}2.5 双重校验锁+volatile
用于处理DCL可能出现的问题(指令重排)
解决方案:只需要给instance的声明加上volatile关键字(volatile--静止指令重排),对它的写操作就会有一个内存屏障。
public class DLCVolatile {private static volatile DLCVolatile instance;private DLCVolatile(){}public static DLCVolatile getInstance() {if (instance==null){synchronized (LazyMan.class){if (instance==null){instance = new DLCVolatile();}}}return instance;}
}2.6 静态内部类
也称为登记类,这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式也利用了类加载机制保证初始化instance时只有一个线程,他和饿汉式不同在于:饿汉式只要单例类被加载,那么instance就会被实例化;而静态内部类类加载后,instance不一定被初始化,因为InnerClass类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载InnerClass类,从而实例化instance。
//静态内部类
public class Holder {private Holder(){}public static Holder getInstance(){return InnerClass.INSTANCE ;}public static class InnerClass{private static final Holder INSTANCE = new Holder();}
}2.7 枚举
实现单例模式的最佳方法,简洁,自动支持序列化机制,防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,在实际工作中很少用。
📌 实现方式
public enum EnumSignle {INSTANCE;public EnumSignle getInstance(){return INSTANCE;}
}class Test{public static void main(String[] args) {EnumSignle enumSignle1 = EnumSignle.INSTANCE;EnumSignle enumSignle2 = EnumSignle.INSTANCE;System.out.println(enumSignle1);System.out.println(enumSignle2);}
}控制台输出:

📌 通过枚举将已有类改造为单例类
public class Singleton {private Singleton(){}public static enum EnumSignle1 {INSTANCE;private Singleton instance = null;private EnumSignle1(){instance = new Singleton();}public Singleton getInstance(){return instance;}}
}class Test1{public static void main(String[] args) {Singleton singleton1 = Singleton.EnumSignle1.INSTANCE.getInstance();Singleton singleton2 = Singleton.EnumSignle1.INSTANCE.getInstance();System.out.println(singleton1 ==singleton2);}
}2.8 ThreadLocal--线程安全
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
public class TLSingleton {private static final ThreadLocal<TLSingleton> tlSingleton = new ThreadLocal<TLSingleton>(){@Overrideprotected TLSingleton initialValue() {return new TLSingleton();}};private TLSingleton(){}public static TLSingleton getInstance() {return tlSingleton.get();}
}2.9 CAS锁实现(线程安全)
public class CASSingleton {private CASSingleton(){}private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<CASSingleton>();public static CASSingleton getInstance() {for (; ; ) {CASSingleton current = INSTANCE.get();if (current != null) {return current;}current = new CASSingleton();if (INSTANCE.compareAndSet(null, current)) {return current;}}}
}📌 总结
一般情况下,不建议使用第1种和第2种懒汉方式,建议使用第3种饿汉方式。只有在要明确实现lazy loading效果时,才会使用第6种静态内部类方式。如果涉及到反序列化创建对象时,可以尝试使用第7种枚举方式。如果有其他特殊的需求,可以考虑使用第4种双检锁方式。
相关文章:
【Java开发】设计模式 01:单例模式
1 单例模式介绍单例模式(Singleton Pattern)是Java中最为基础的设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对…...
10、go工程化与标准库
目录一、用go mod管理工程二、包引入规则三、init调用链四、可见性五、标准库1 - 时间函数2 - 数学计算3 - I/O操作4 - 编码一、用go mod管理工程 初始化项目:go mod init $module_name,$module_name和目录名可以不一样。上述命令会生成go.mod文件 mod…...
【Selenium自动化测试】鼠标与键盘操作
在 WebDriver 中,与鼠标操作相关的方法都封装在ActionChains 类中,与键盘操作相关的方法都封装在Keys类中。下面介绍下这两个类中的常用方法。 鼠标操作 ActionChains类鼠标操作常用方法: context_click():右击double_click()&…...
自定义javax.validation校验枚举类
枚举类单一情况 package com.archermind.cloud.phone.dto.portal.external.validation.validator;import com.archermind.cloud.phone.dto.portal.external.validation.constraints.EnumValidation; import lombok.extern.slf4j.Slf4j;import javax.validation.ConstraintVali…...
[Java·算法·中等]LeetCode39. 组合总和
每天一题,防止痴呆题目示例分析思路1题解1分析思路2题解2👉️ 力扣原文 题目 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形…...
【Linux】vi和vim编辑器
目录主题主题 三种常见模式: 正常模式 以vim 打开一个档案就直接进入一般模式了(这是默认的模式)。在这个模式中,你可以使用[上下左右]按键来移动光标,你可以使用『删除字符』或『删除整行』来处理档案内容,也可以使用「复制、…...
BIO,NIO,AIO
IO模型 用什么样的通道进行数据传输和接收,java支持3种io网络编程模式 BIO NIO AIO BIO 同步阻塞 一个客户端连接对应一个处理线程 BIO示例代码(客户端和服务端) package com.tuling.bio;import java.io.IOException; import java.net.So…...
代码随想录刷题-数组-有序数组的平方
文章目录有序数组的平方习题暴力排序双指针有序数组的平方 本节对应代码随想录中:代码随想录,讲解视频:有序数组的平方_哔哩哔哩_bilibili 习题 题目链接:977. 有序数组的平方 - 力扣(LeetCode) 给你一…...
【玩转c++】stack和queue的介绍和模拟实现
本期主题:list的讲解和模拟实现博客主页: 小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限,出现错误希望大家不吝赐stack的介绍和使用1.1.stack的介绍1. stack是一种容器适配器,专门用在具有后进先出操作的上…...
Linux order(文件、磁盘、网络、系统管理、备份压缩)
1. Linux 文件命令 -rwxrwxrwx chmod:change mode,用于(文件所有者或 root )变更用户(u:owner g:group o:other a:all)的权限 chmod [OPTION]… MODE[,MODE]… FILE… OPTION -R:递归修改more option:chmod…...
最详细的CentOS7安装Mysql数据库服务
1.查看是否安装mysql: rpm -qa | grep mysql如果有查出来东西,使用命令删除: rpm -e xxx2.检查是否有mysql用户组和mysql用户,没有就添加有就忽略: groups mysql 添加用户组和用户 groupadd mysql && useradd -r -g mysql mysql&a…...
【IoT】项目管理:如何做好端到端的项目管理?
今天主要来谈谈项目管理这个话题。 首先来看一个我在网络上看到的一个关于项目管理的案例或者是段子。 将项目管理的作用及意义非常直观地展示了出来。 有一个植树搞绿化的企业,在公司内部设置有五个部门,分别是: 运输部门;挖坑部…...
渲染十万条数据就把你难住了?不存在的!
虚拟列表的使用场景如果我想要在网页中放大量的列表项,纯渲染的话,对于浏览器性能将会是个极大的挑战,会造成滚动卡顿,整体体验非常不好,主要有以下问题:页面等待时间极长,用户体验差CPU计算能力…...
编程学习的心路历程和困惑回顾
回首入行9年的经历,从大一开始学习C语言和数据结构,老师一直是在用IDE演示程序的编写和运行,我们也就一直在跟黑乎乎的命令行窗口打交道。 后来在一些课程的实验环节,接触到了一些别人编写好的工程代码,知道了Makefile…...
请介绍类加载过程,什么是双亲委派模型?
第23讲 | 请介绍类加载过程,什么是双亲委派模型? Java 通过引入字节码和 JVM 机制,提供了强大的跨平台能力,理解 Java 的类加载机制是深入 Java 开发的必要条件,也是个面试考察热点。 今天我要问你的问题是࿰…...
Navisworks编辑材质和Revit快速切换材质问题
一、如何在Navisworks2016中编辑材质 初次使用NW2016-2017时发现,原来用于创建编辑材质的小地球不见了,如图1所示,在各大技术群里求助没有回应,度娘搜索也总是摇头。 经过仔细排查可能出现的地方,终于找到了可以编辑材…...
Object对象键值的输出循序到底如何排列的?
1.日常摸鱼看八股 今天又是复习八股文的一天,发现还是彻底懂得原理才好和面试官吹牛批呀。 接着来看看我chat大宝贝的回答: 在现代浏览器中,Object 对象的键值输出循序是比较稳定的,通常是按照如下顺序输出: 所有的数…...
气泡式水位计的安装方法详解
气泡水位计的安装实际上就是气管的安装,气管的安装是否正确将直接影响到仪器测量数据的结果,气泡水位计它由活塞泵产生的压缩空气流经测量管和气泡室,进入被测的水体中,测量管中的静压力与气泡室上的水位高度成正比。那么接下来就…...
求“二维随机变量的期望E(X)与方差D(X)”例题(一)
离散型 设随机变量(X,Y)的联合分布律为 X\Y0100.10.210.30.4 (1)求E(X) 先求x的边缘分布律,表格里x0的概率为0.10.2,于是我们可得 X01P0.30.7直接求E(X)即可,得到结果 (2)求E(XY) 直接x与y相乘就行。 记得别乘多了,别的算了又…...
MySQL 搞定行转列,列转行
行转列方法总结1、使用case…when…then2、使用SUM(IF()) 生成列3、使用SUM(IF()) 生成列 WITH ROLLUP 生成汇总行4、使用SUM(IF()) 生成列 UNION 生成汇总行,并利用 IFNULL将汇总行标题显示为 Total5、使用SUM(IF()) 生成列,直接生成汇总结果,不再利用…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
