单例设计模式(Singleton Design Pattern) 是软件开发中最常用的设计模式之一,属于 创建型设计模式。单例模式确保一个类在整个应用程序生命周期中只有一个实例,并提供一个全局访问点。
单例模式是一种设计模式,其目标是确保某个类只有一个实例,并为其他代码提供一个全局访问点。该模式主要解决的问题是 控制实例数量并提供统一的访问接口。
单例模式在 Java 中的实现可以分为以下几种方式:
懒汉式实现会在第一次需要单例实例时才进行创建,这种方式可以延迟初始化,但需要注意线程安全问题。
public class Singleton {
private static Singleton instance;
//构造方法私有化
private Singleton(){}
//提供公共接口
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
为了解决懒汉式的线程安全问题,可以对 getInstance()
方法加锁:
public class Singleton {
private static Singleton instance;
//构造方法私有化
private Singleton(){}
//提供公共接口
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
synchronized
锁会导致每次调用 getInstance()
都需要加锁,即使实例已经创建。双重检查锁是一种优化方案,通过减少锁的粒度来提升性能,仅在实例未初始化时加锁。
public class Singleton {
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if (instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
volatile
关键字来防止指令重排。在 Java 中,volatile
关键字用来确保变量在多个线程间的可见性,并防止 JVM 对变量的读写进行指令重排。通过 volatile
,可以确保读取到的变量是最新值,从而避免出现不一致的问题。
饿汉式在类加载时就创建实例,不存在线程安全问题,但会浪费资源。
public class Singleton {
//类加载时实例化对象
private static final Singleton instance = new Singleton();
private Singleton(){}
private static Singleton getInstance(){
return instance;
}
}
静态内部类方式结合了懒汉式和饿汉式的优点,既支持延迟加载,又是线程安全的。
public class Singleton {
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton instance(){
return SingletonHolder.INSTANCE;
}
}
getInstance()
时才会初始化。使用枚举是实现单例模式的最佳方式,还能够避免反序列化和反射攻击。
public enum Singleton {
instance;
public Singleton getInstance(){
return instance;
}
}
单例模式广泛用于需要共享资源或全局管理的场景,以下是一些典型的应用:
配置管理:
例如,读取配置文件的类需要全局唯一,以便统一管理配置。日志记录:
日志记录类通常设计为单例模式,以确保日志文件或输出流的唯一性。数据库连接池:
数据库连接池是一个全局共享资源,通常用单例模式实现。线程池管理:
线程池需要全局管理,防止重复创建和销毁线程资源。操作系统资源管理:
如窗口管理器、文件系统、打印机管理器等。缓存管理:
缓存类作为全局资源,确保所有模块共享相同的数据。线程安全:
在多线程环境中,懒汉式实现需要特别注意线程安全问题。反序列化漏洞:
使用Serializable
接口时,反序列化可能会创建新的实例。解决方法是重写 readResolve()
方法: protected Object readResolve() {
return getInstance();
}
反射攻击:
反射可以通过私有构造方法创建多个实例。防御方法:private Singleton() {
if (instance != null) {
throw new RuntimeException("Instance already created");
}
}
适用性:
单例模式适合需要全局唯一实例的场景,但滥用可能导致代码难以测试和扩展。