23种设计模式——1. 单例模式
概念
单例模式:在计算机进程中,同一个类始终只有一个对象进行操作
多例模式:在计算机进程中,对一个实体类创建异常对象就是单个对象操作;若创建多个对象则分别对应多个对象操作。
应用场景
- 数据连接池管理 确保整个应用共享同一个连接池,避免频繁创建/销毁连接池带来的性能损耗。
- 日志记录系统 单例日志保证所有模块写入同一日志文件,防止多实例导致的日志混乱或文件冲突。
- Windows任务管理器 操作系统只能打卡一个任务管理器窗口,通过单例模式实现进程监控唯一入口。
使用
单例模式包含以下写法:
| 模式 | 说明 | 建议/不建议原因 |
|---|---|---|
| 饿汉模式 ❎ | 最开始时就创建对象 | 未实现懒加载,可能造成资源浪费 |
| 懒汉模式(非线程安全)❎ | 首次调用时创建实例 | 多线程环境下可能会创建多个实例 |
| 加锁的懒汉式单例(synchronized )❎ | 首次使用的时候创建对象。但需要防止多线程,所以需要上锁。 | 同步锁导致性能下降。即使示例已经创建。 |
| 懒汉模式:双重检测说模式 ❎ | 首次使用的时候创建对象,同时防止多线程。如果单例已创建,则,则不上锁。 | 对比加载的懒汉式单例,性能得到了提升。适合创建时性能开销比较少的场景 |
| 静态内部类✅ | 属于半懒汉、半饿汉式的单例 静态内部类开始时不会加载,需要的时候才会加载,由于这个类一加载就会创建对象。所以实现了懒汉的资源不滥用,饿汉的防止多线程 |
没有资源浪费和多线程问题。(不过无法防止反射攻击创建新实例) |
| 枚举单例✔️ | 通过枚举类实现单例 | 线程安全且代码简洁;不适用于继承其他类;防止反射攻击 |
-
饿汉模式❎
/** * 单例模式1:饿汉式 (不建议) * 优点:线程安全,在类加载时就创建了单例对象,不会出现线程安全问题。 * 缺点:如果单例对象没有被使用,就浪费了内存。 * @Author:lyj * @Date:2025/4/28 15:53 */ public class Singleton1 { private static Singleton1 instance = new Singleton1(); // 引用全局唯一的单例对象,在一开始就创建好 /** * 私有构造函数,防止外部创建实例 */ private Singleton1() { } public static Singleton1 getInstance() { // 获取唯一的单例对象 return instance; } } -
懒汉模式 ❎
/** - 单例模式2:懒汉模式 - @Author:lyj - @Date:2025/4/28 16:17 - @Filename:Singleton2 */ public class Singleton2 { private static Singleton2 instance; /** - 私有构造函数,防止外部创建实例 */ private Singleton2(){ } public static Singleton2 getInstance(){ //如果实例为空,则创建实例 ;否则直接返回实例 if(instance == null){ instance = new Singleton2(); } return instance; } public static void main(String[] args) { }以下语句,可能造成多个对象。
if(instance == null){ instance = new Singleton2(); }另一个线程可能在instance创建前,同时判断instance为空,也创建了一个对象。为了更好的展示,可以把方法改为:
if(instance == null){ try { Thread.sleep(1000); // 模拟耗时操作 } catch (InterruptedException e) { throw new RuntimeException(e); } instance = new Singleton2();此时,多线程示例:
// 测试多线程下的单例模式 for (int i = 0; i < 10; i++) { new Thread(() -> { Singleton2 instance = Singleton2.getInstance(); System.out.println(instance); }).start(); }可以发现,创建了不同的对象。
![image]()
-
加锁的懒汉模式 (synchronized)❎
/** * 单例模式:加锁懒汉模式 (synchronized) * @Author:lyj * @Date:2025/4/28 19:10 */ public class Singleton3 { private static volatile Singleton3 instance; // volatile修饰,表示该变量在多线程下可见 private Singleton3(){} // 禁用构造方法 public static synchronized Singleton3 getInstance(){ if(instance==null){ // 第一次外访问时不加锁 instance = new Singleton3(); } return instance; } } -
懒汉模式:双重检测锁定(DCL) ❎
/** 懒汉模式:双重检测机制(DCL) * @Author:lyj * @Date:2025/5/4 10:06 */ public class Singleton4 { private static volatile Singleton4 instance; private Singleton4(){} public static Singleton4 getInstance(){ if(instance == null){ // 第一次外访问 synchronized (Singleton4.class){ // 加锁 if(instance == null){ // 第两次内访问 instance = new Singleton4(); // 新建 } } } return instance; } } -
静态内部类 ✔️
/** * 单例模式:静态内部类 * @Author:lyj * @Date:2025/5/4 10:39 */ public class Single5 { // 私有化构造方法 private Single5(){} private static class SingletonHolder{ // 由静态内部类创建单例。 private static final Single5 INSTANCE = new Single5(); } public static Single5 getInstance(){ // 只有首次使用内部类时,才会进行类初始化 return SingletonHolder.INSTANCE; } }静态内部类刚开始时不会加载,需要的时候才会加载。由于这个类一加载就会创建对象。所以,实现了懒汉的资源不滥用,饿汉式防止多线程
-
枚举单例✔️
/**- 单例: 枚举单例 - @Author:lyj - @name:Singleton6 - @Date:2025/5/4 11:56 */ public enum Singleton6 { INSTANCE; // 唯一实例,由JVM线程安全 // 私有构造函数 private Singleton6() {} /** - 公共方法 */ public void doSomething() { System.out.println("doSomething"); } }枚举单例以及简的代码实现高性能,安全的单例模式。虽然,是不是严格的延迟加载,但其综合优势远超其他实现,适合绝大多数的场景。
私有构造函数,防止实例化
在以上单例的例子中,禁用构造函数的方式都是直接使用私有化。已知,私有的方法,外部直接使用 Singleton1 singleton1 = new Singleton1();是错误的。如果使用映射。
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.lyj.singleton.Singleton1");
Constructor<?> constructor = clazz.getDeclaredConstructor(); // 创建无参构造函数
constructor.setAccessible(true);
for (int i = 0; i < 10; i++){
Singleton1 instance1 = (Singleton1) constructor.newInstance(); // 创建实例
System.out.println("i=>" + i + ": " + instance1);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
运行后可以发现,是不同的单例。

所以,可以在私有化方法中加入判断。
/**
* 私有构造函数,防止外部创建实例
*/
private Singleton1() {
if (instance != null) {
throw new IllegalArgumentException("单例对象已经创建,禁止重复创建");
}
}
有志者,事竟成,破釜沉舟,百二秦关终属楚; 苦心人,天不负,卧薪尝胆,三千越甲可吞吴。


浙公网安备 33010602011771号