文章目录
- 前言
- 一、概念
- 二、核心思想
- 三、常见实现方式(Java代码)
-
- [1. 饿汉式(立即加载)](#1. 饿汉式(立即加载))
- [2. 懒汉式(延迟加载,线程不安全)](#2. 懒汉式(延迟加载,线程不安全))
- [3. 懒汉式(同步方法,线程安全)](#3. 懒汉式(同步方法,线程安全))
- [4. 双重检查锁(DCL,推荐)](#4. 双重检查锁(DCL,推荐))
- [5. 枚举单例(最优解)](#5. 枚举单例(最优解))
- [6. 测试类](#6. 测试类)
- 四、优缺点
-
- [1. 优点](#1. 优点)
- [2. 缺点](#2. 缺点)
- 五、应用场景
- 六、注意事项
- 总结
前言
在AI时代,代码的编写可以被大模型辅助甚至替代,但程序员真正的核心竞争力是技术思维!设计模式这类沉淀了数十年的"内功心法",决定了代码的可维护性、扩展性和稳定性,是AI无法完全替代的核心能力。单例模式作为创建型模式的入门典范,看似简单却暗藏细节,是每个Java开发者必须吃透的基础。
一、概念
单例模式(Singleton Pattern)是一种创建型设计模式,核心目标是保证一个类在整个应用程序中只有一个实例,并提供一个全局访问点。它通过控制对象的创建过程,避免重复实例化导致的资源浪费(如数据库连接池、配置类),同时确保全局状态的一致性。
二、核心思想
- 私有化构造方法 :禁止外部通过
new关键字创建实例,从根源上杜绝多实例; - 私有静态实例:在类内部维护唯一的实例对象;
- 公共静态方法:提供全局访问入口,确保外部只能通过该方法获取实例。
单例模式的核心本质是控制实例的生命周期,将对象的创建权收归类自身,而非外部调用者。
三、常见实现方式(Java代码)
单例模式有多种实现方式,不同方式对应不同的线程安全、性能和初始化策略,以下是最常用的4种:
1. 饿汉式(立即加载)
java
/**
* 饿汉式单例:类加载时直接初始化实例,线程安全
* 优点:实现简单、天然线程安全;缺点:类加载时就创建实例,可能造成资源浪费
*/
public class HungrySingleton {
// 1. 私有静态最终实例(类加载时初始化,JVM保证线程安全)
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 2. 私有化构造方法,禁止外部new
private HungrySingleton() {
// 防止反射破坏单例(可选增强)
if (INSTANCE != null) {
throw new RuntimeException("禁止重复创建单例实例");
}
}
// 3. 公共静态方法,提供全局访问点
public static HungrySingleton getInstance() {
return INSTANCE;
}
// 测试方法
public void sayHello() {
System.out.println("饿汉式单例:" + this.hashCode());
}
}
2. 懒汉式(延迟加载,线程不安全)
java
/**
* 懒汉式单例:首次调用时初始化实例,基础版本线程不安全
* 优点:延迟加载,节省资源;缺点:多线程下可能创建多个实例
*/
public class LazySingleton {
// 1. 私有静态实例(未初始化)
private static LazySingleton INSTANCE;
// 2. 私有化构造方法
private LazySingleton() {}
// 3. 公共静态方法,首次调用时创建实例
public static LazySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazySingleton();
}
return INSTANCE;
}
public void sayHello() {
System.out.println("懒汉式单例:" + this.hashCode());
}
}
3. 懒汉式(同步方法,线程安全)
java
/**
* 懒汉式单例:同步方法保证线程安全
* 优点:线程安全、延迟加载;缺点:synchronized锁粒度大,性能低
*/
public class SyncLazySingleton {
private static SyncLazySingleton INSTANCE;
private SyncLazySingleton() {}
// 加synchronized关键字,保证多线程下只有一个线程进入
public static synchronized SyncLazySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new SyncLazySingleton();
}
return INSTANCE;
}
public void sayHello() {
System.out.println("同步懒汉式单例:" + this.hashCode());
}
}
4. 双重检查锁(DCL,推荐)
java
/**
* 双重检查锁(DCL)单例:兼顾延迟加载、线程安全和性能
* 优点:线程安全、延迟加载、性能高;缺点:实现稍复杂,需注意volatile关键字
*/
public class DCLSingleton {
// volatile关键字:禁止指令重排,保证INSTANCE初始化完成后再被读取
private static volatile DCLSingleton INSTANCE;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
// 第一层检查:避免每次调用都加锁
if (INSTANCE == null) {
// 加锁:保证只有一个线程进入初始化流程
synchronized (DCLSingleton.class) {
// 第二层检查:防止多个线程等待锁后重复创建
if (INSTANCE == null) {
INSTANCE = new DCLSingleton();
}
}
}
return INSTANCE;
}
public void sayHello() {
System.out.println("DCL单例:" + this.hashCode());
}
}
5. 枚举单例(最优解)
java
/**
* 枚举单例:JVM天然保证单例,避免反射和序列化破坏
* 优点:实现最简单、绝对线程安全、防反射/序列化;缺点:无法延迟加载
*/
public enum EnumSingleton {
// 唯一实例
INSTANCE;
public void sayHello() {
System.out.println("枚举单例:" + this.hashCode());
}
}
6. 测试类
java
public class SingletonTest {
public static void main(String[] args) {
// 饿汉式测试
HungrySingleton hungry1 = HungrySingleton.getInstance();
HungrySingleton hungry2 = HungrySingleton.getInstance();
System.out.println(hungry1 == hungry2); // true
// DCL测试
DCLSingleton dcl1 = DCLSingleton.getInstance();
DCLSingleton dcl2 = DCLSingleton.getInstance();
System.out.println(dcl1 == dcl2); // true
// 枚举测试
EnumSingleton enum1 = EnumSingleton.INSTANCE;
EnumSingleton enum2 = EnumSingleton.INSTANCE;
System.out.println(enum1 == enum2); // true
}
}
四、优缺点
1. 优点
- 节省资源:避免重复创建对象,减少内存占用和GC压力(如数据库连接池、配置类);
- 全局一致性:确保全局只有一个实例,避免多实例导致的状态不一致;
- 简化访问:提供统一的全局访问点,无需频繁传递对象引用。
2. 缺点
- 违背单一职责原则:单例类既负责业务逻辑,又负责控制实例创建;
- 扩展性差:单例模式本质是"硬编码"的全局变量,若后续需要多实例,修改成本高;
- 测试困难:单例依赖难以模拟,单元测试时无法灵活替换实例;
- 可能引发内存泄漏:单例实例长期存活,若持有外部引用(如Context),可能导致内存泄漏。
五、应用场景
单例模式适用于需要全局唯一实例、创建成本高、无需多实例的场景:
- 工具类/配置类 :如Spring中的
Environment、项目中的全局配置类; - 资源管理类:数据库连接池、Redis连接池、线程池(避免重复创建连接/线程);
- 日志类:全局日志管理器,确保日志写入的一致性;
- 缓存类:全局缓存实例,避免多缓存实例导致数据不一致;
- 框架核心类 :Spring容器中的Bean默认单例、Java的
Runtime类(Runtime.getRuntime())。
六、注意事项
- 线程安全 :懒汉式基础版本在多线程下会创建多个实例,必须通过
synchronized或DCL保证线程安全; - 反射破坏:可通过在构造方法中检查实例是否已存在,防止反射创建多实例;
- 序列化破坏 :实现
Serializable接口时,需重写readResolve()方法,返回唯一实例; - 延迟加载:若实例创建成本高且不一定被使用,优先选择DCL或懒汉式;若实例必须立即初始化,选择饿汉式或枚举。
总结
单例模式是最简单的设计模式之一,却体现了"控制对象创建"的核心设计思想。在AI时代,掌握单例模式不仅是写出"正确代码",更是理解"为何这么设计"------这正是程序员区别于AI的核心竞争力。选择单例模式时,需根据场景权衡延迟加载、线程安全和性能,优先推荐枚举单例(简单安全)或DCL(兼顾性能)。