免费版Java学习笔记(38w字)链接:https://www.yuque.com/aoyouaoyou/sgcqr8 免费版Java面试题(28w字)链接:https://www.yuque.com/aoyouaoyou/wh3hto 完整版可在小红书搜索:遨游qk0
单例模式是创建型设计模式中最常用的一种,核心是保证某个类在程序运行期间仅有一个实例,并提供全局统一的访问入口。适用于日志工具、配置管理、数据库连接池等需避免重复创建的场景,也是面试高频考点。
此处为语雀内容卡片,点击链接查看:https://www.yuque.com/aoyouaoyou/pw1w5m/grpkf5br1h3qc2yr
代码位置:

一、单例模式定义与目标
1. 定义
确保一个类在整个应用生命周期中只有一个实例,且该实例能被全局访问,避免重复创建对象导致的资源浪费(如内存、CPU)。
2. 目标
- 限制实例数量:仅允许创建一个实例;
- 全局访问:提供统一的静态方法供外部获取实例;
- 线程安全:多线程环境下仍能保证实例唯一性。
二、单例模式6种实现方式

1. 饿汉式(预加载)
核心原理
类加载时直接初始化静态实例,由JVM保证线程安全(类加载过程是单线程的)。
代码实现
/**
* 饿汉式单例:类加载时创建实例,不支持懒加载
*/
public class CityServiceCenter {
// 1. 私有构造方法:禁止外部通过new创建实例
private CityServiceCenter() {}
// 2. 私有静态实例:类加载时初始化(预加载)
private static final CityServiceCenter INSTANCE = new CityServiceCenter();
// 3. 全局访问方法:返回唯一实例
public static CityServiceCenter getInstance() {
return INSTANCE;
}
// 示例业务方法
public void handleBusiness(String userName) {
System.out.println(userName + " 正在市政服务中心办理业务");
}
}
┌─────────────────────────────────────────────────────────────┐
│ CityServiceCenter │
├─────────────────────────────────────────────────────────────┤
│ – INSTANCE: CityServiceCenter = new CityServiceCenter() │
├─────────────────────────────────────────────────────────────┤
│ – CityServiceCenter() │
│ + getInstance(): CityServiceCenter │
│ + handleBusiness(String): void │
└─────────────────────────────────────────────────────────────┘
时序流程:
Client → getInstance() → INSTANCE
类加载时已初始化

特点
- 优点:实现简单、线程安全、获取实例速度快;
- 缺点:不支持懒加载(类加载时就创建实例),若实例占用资源大且长期未使用,会造成内存浪费;
- 适用场景:实例体积小、启动时必用的场景(如配置类)。
2. 懒汉式(懒加载-线程不安全)

核心原理
仅当外部调用getInstance()方法时才创建实例,支持懒加载,但多线程环境下存在线程安全问题。
代码实现
/**
* 懒汉式单例:懒加载,线程不安全(多线程下可能创建多个实例)
*/
public class WaterSupplySystem {
// 1. 私有构造方法
private WaterSupplySystem() {}
// 2. 私有静态实例:初始为null,不预加载
private static WaterSupplySystem instance;
// 3. 全局访问方法:调用时才创建实例
public static WaterSupplySystem getInstance() {
// 未加锁:多线程可能同时进入判断,创建多个实例
if (instance == null) {
instance = new WaterSupplySystem();
}
return instance;
}
// 示例业务方法
public void supplyWater(String area) {
System.out.println("为 " + area + " 区域供水");
}
}
┌─────────────────────────────────────────────────────────────┐
│ WaterSupplySystem │
├─────────────────────────────────────────────────────────────┤
│ – instance: WaterSupplySystem = null │
├─────────────────────────────────────────────────────────────┤
│ – WaterSupplySystem() │
│ + getInstance(): WaterSupplySystem │
│ + supplyWater(String): void │
└─────────────────────────────────────────────────────────────┘
时序流程:
Client → getInstance() → if(instance==null) → new instance
多线程下可能创建多个实例
特点
- 优点:支持懒加载,节省内存;
- 缺点:线程不安全(多线程并发调用时可能创建多个实例);
- 适用场景:单线程环境,或不关注线程安全的简单场景(不推荐生产使用)。
3. 懒汉式(懒加载-线程安全)

核心原理
在getInstance()方法上添加synchronized同步锁,强制多线程串行执行,避免并发创建实例。
代码实现
/**
* 懒汉式单例:懒加载+同步锁,线程安全但性能低
*/
public class PowerGridControl {
// 1. 私有构造方法
private PowerGridControl() {}
// 2. 私有静态实例
private static PowerGridControl instance;
// 3. 同步方法:保证多线程串行调用
public static synchronized PowerGridControl getInstance() {
if (instance == null) {
instance = new PowerGridControl();
}
return instance;
}
// 示例业务方法
public void adjustPower(String region) {
System.out.println("调整 " + region + " 区域电网负荷");
}
}
┌─────────────────────────────────────────────────────────────┐
│ PowerGridControl │
├─────────────────────────────────────────────────────────────┤
│ – instance: PowerGridControl = null │
├─────────────────────────────────────────────────────────────┤
│ – PowerGridControl() │
│ + getInstance(): synchronized PowerGridControl │
│ + adjustPower(String): void │
└─────────────────────────────────────────────────────────────┘
时序流程:
Thread1 → getInstance() → [LOCK] → if(instance==null) → new instance
Thread2 → 等待锁释放 → [LOCK] → 直接返回instance
特点
- 优点:线程安全、支持懒加载;
- 缺点:性能瓶颈(同步锁导致多线程串行执行,并发度为1);
- 适用场景:低并发场景,对性能要求不高的业务。
4. 双重校验锁(DCL:Double-Check Locking)


核心原理
结合“懒加载+同步代码块+volatile关键字”,既保证线程安全,又提升并发性能(仅初始化时加锁,后续获取实例无需锁)。
代码实现
/**
* 双重校验锁单例:懒加载+高并发+线程安全(推荐生产使用)
*/
public class TrafficControlSystem {
// 1. 私有构造方法
private TrafficControlSystem() {}
// 2. 私有静态实例:volatile关键字禁止指令重排序
private static volatile TrafficControlSystem instance;
// 3. 双重校验+同步代码块
public static TrafficControlSystem getInstance() {
// 第一次校验:避免已创建实例后仍进入同步代码块(提升性能)
if (instance == null) {
// 同步锁:锁定类对象,保证初始化时单线程执行
synchronized (TrafficControlSystem.class) {
// 第二次校验:防止多线程同时等待锁后重复创建实例
if (instance == null) {
instance = new TrafficControlSystem();
}
}
}
return instance;
}
// 示例业务方法
public void controlTrafficLight(String intersection) {
System.out.println("控制 " + intersection + " 路口红绿灯");
}
}
┌─────────────────────────────────────────────────────────────┐
│ TrafficControlSystem │
├─────────────────────────────────────────────────────────────┤
│ – instance: volatile TrafficControlSystem = null │
├─────────────────────────────────────────────────────────────┤
│ – TrafficControlSystem() │
│ + getInstance(): TrafficControlSystem │
│ + controlTrafficLight(String): void │
└─────────────────────────────────────────────────────────────┘
时序流程:
Thread1 → getInstance() → if(instance==null) → [LOCK] → if(instance==null) → new instance
Thread2 → getInstance() → if(instance!=null) → 直接返回instance (无锁)
关键说明:volatile关键字的作用
instance = new TrafficControlSystem() 并非原子操作,JVM会拆分为3步:
若无volatile,JVM可能指令重排序(执行顺序变为1→3→2),导致线程A未完成初始化时,线程B读取到非null但未初始化的实例,引发空指针异常。volatile可禁止指令重排序,保证实例初始化完整。
特点
- 优点:线程安全、支持懒加载、高并发性能好;
- 缺点:实现稍复杂,需理解volatile关键字作用;
- 适用场景:高并发生产环境(最常用的实现方式)。
5. 静态内部类(推荐)

核心原理
利用静态内部类的特性:外部类加载时不会加载内部类,仅当调用getInstance()时才加载内部类并初始化实例,由JVM保证线程安全。
代码实现
/**
* 静态内部类单例:懒加载+线程安全+实现简洁(推荐生产使用)
*/
public class EnvironmentalMonitor {
// 1. 私有构造方法
private EnvironmentalMonitor() {}
// 2. 静态内部类:仅当被调用时才加载
private static class MonitorHolder {
// 内部类中初始化外部类实例
private static final EnvironmentalMonitor INSTANCE = new EnvironmentalMonitor();
}
// 3. 全局访问方法:触发内部类加载
public static EnvironmentalMonitor getInstance() {
return MonitorHolder.INSTANCE;
}
// 示例业务方法
public void detectAirQuality(String city) {
System.out.println("检测 " + city + " 空气质量:优");
}
}
┌─────────────────────────────────────────────────────────────┐
│ EnvironmentalMonitor │
├─────────────────────────────────────────────────────────────┤
│ – EnvironmentalMonitor() │
├─────────────────────────────────────────────────────────────┤
│ + getInstance(): EnvironmentalMonitor │
│ + detectAirQuality(String): void │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ static class MonitorHolder │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ – INSTANCE: EnvironmentalMonitor = new instance │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
时序流程:
Client → getInstance() → MonitorHolder.INSTANCE
首次调用时加载内部类并初始化实例
特点
- 优点:线程安全(JVM加载内部类时单线程)、支持懒加载、实现简洁、无性能损耗;
- 缺点:无法通过反射防止实例破坏(需额外处理);
- 适用场景:大多数生产环境,兼顾简洁性与性能。
6. 枚举单例(《Effective Java》推荐)
此处为语雀内容卡片,点击链接查看:https://www.yuque.com/aoyouaoyou/pbz18g/hx9h4u1ufedbkn4a

核心原理
利用Java枚举的特性:枚举实例默认线程安全、仅初始化一次,且天然防止反射和序列化破坏单例。
代码实现
/**
* 枚举单例:线程安全+防反射+防序列化(最稳定的实现方式)
*/
public enum WeatherForecastSystem {
// 唯一枚举实例(等价于单例实例)
INSTANCE;
// 枚举类可定义属性和方法
private String latestForecast;
// 示例业务方法
public void updateForecast(String forecast) {
this.latestForecast = forecast;
System.out.println("更新天气预报:" + forecast);
}
public String getLatestForecast() {
return latestForecast;
}
}
┌─────────────────────────────────────────────────────────────┐
│ WeatherForecastSystem (enum) │
├─────────────────────────────────────────────────────────────┤
│ INSTANCE │
│ – latestForecast: String │
├─────────────────────────────────────────────────────────────┤
│ + updateForecast(String): void │
│ + getLatestForecast(): String │
└─────────────────────────────────────────────────────────────┘
使用方式:
WeatherForecastSystem.INSTANCE.updateForecast("晴")
特点
- 优点:
- 缺点:不支持懒加载(枚举类加载时初始化实例);
- 适用场景:对单例稳定性要求极高的场景(如核心配置、支付系统)。
三、单例模式的破坏与解决方案
1. 反射破坏及防护
问题描述
通过Java反射机制,可强制访问私有构造方法,创建多个实例。
防护方案(以静态内部类为例)
在构造方法中添加判断,若实例已存在则抛出异常:
private EnvironmentalMonitor() {
// 防护反射破坏:若实例已存在,抛出异常
if (MonitorHolder.INSTANCE != null) {
throw new IllegalStateException("单例类禁止重复创建实例");
}
}
2. 序列化破坏及防护
问题描述
实现Serializable接口的单例类,序列化后再反序列化,会创建新实例(破坏单例)。
防护方案
添加readResolve()方法,指定反序列化时返回已有的单例实例:
public class EnvironmentalMonitor implements Serializable {
// 原有代码不变…
// 反序列化时调用,返回单例实例
private Object readResolve() {
return MonitorHolder.INSTANCE;
}
}
package com.ao.a_singleton.demo;
import java.io.Serializable;
/**
* 静态内部类单例:懒加载+线程安全+实现简洁(推荐生产使用)
*/
public class EnvironmentalMonitor implements Serializable {
// 1. 私有构造方法
//private EnvironmentalMonitor() {}
private EnvironmentalMonitor() {
// 防护反射破坏:若实例已存在,抛出异常
if (MonitorHolder.INSTANCE != null) {
throw new IllegalStateException("单例类禁止重复创建实例");
}
}
// 2. 静态内部类:仅当被调用时才加载
private static class MonitorHolder {
// 内部类中初始化外部类实例
private static final EnvironmentalMonitor INSTANCE = new EnvironmentalMonitor();
}
// 3. 全局访问方法:触发内部类加载
public static EnvironmentalMonitor getInstance() {
return MonitorHolder.INSTANCE;
}
// 示例业务方法
public void detectAirQuality(String city) {
System.out.println("检测 " + city + " 空气质量:优");
}
// 反序列化时调用,返回单例实例
private Object readResolve() {
return MonitorHolder.INSTANCE;
}
}
┌─────────────────────────────────────────────────────────────┐
│ EnvironmentalMonitor │
│ implements Serializable │
├─────────────────────────────────────────────────────────────┤
│ – EnvironmentalMonitor() │
│ ↳ 反射防护: if(instance存在) throw Exception │
├─────────────────────────────────────────────────────────────┤
│ + getInstance(): EnvironmentalMonitor │
│ – readResolve(): Object (序列化防护) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ static class MonitorHolder │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ – INSTANCE: EnvironmentalMonitor │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
四、6种实现方式对比总结
|
实现方式 |
线程安全 |
懒加载 |
防反射 |
防序列化 |
适用场景 |
|
饿汉式 |
是 |
否 |
否 |
否 |
实例体积小、启动必用 |
|
懒汉式(无锁) |
否 |
是 |
否 |
否 |
单线程测试场景 |
|
懒汉式(同步方法) |
是 |
是 |
否 |
否 |
低并发、对性能无要求 |
|
双重校验锁 |
是 |
是 |
否 |
否 |
高并发生产环境 |
|
静态内部类 |
是 |
是 |
否 |
否 |
大多数生产环境(推荐) |
|
枚举单例 |
是 |
否 |
是 |
是 |
高稳定性核心业务(推荐) |
五、使用建议
网硕互联帮助中心





评论前必须登录!
注册