【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()) 生成列,直接生成汇总结果,不再利用…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
