设计模式 - 单例(singleton)

单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供全局访问点来获取该实例。核心是通过私有化构造函数、静态方法和静态变量控制实例化过程。


 解决的问题

  • 资源重复创建问题(如数据库连接池、日志管理器)。
  • 多个实例导致数据不一致问题(如配置管理器)。

 特点

  • 私有构造函数:禁止外部通过new创建实例。
  • 静态实例:通过静态变量持有唯一实例。
  • 延迟加载:部分实现中实例在首次访问时创建。
  • 线程安全:多线程环境下需保证实例唯一性。

 编码实现

基础版(线程不安全):
public class Singleton {
    private static Singleton instance;

    private Singleton() {} // 私有构造

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
线程安全版(双重检查锁):
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 内存模型定义了线程如何与主内存(Main Memory)和本地工作内存(Working Memory)交互。每个线程都有一个独立的工作内存

  • 线程内部缓存(本地工作内存):每个线程在运行时会有一个独立的工作内存​(线程本地缓存),用于存储从主内存中拷贝的变量副本。

  • 主内存(Main Memory)​:存储所有共享变量(如堆中的对象、静态变量)。

  • 线程对变量的操作(读取或修改)通常直接作用于工作内存中的副本,而非直接操作主内存中的变量。


实践应用

场景1:配置管理器
  • 需求:全局共享配置信息,避免重复读取配置文件。
  • 实现:
public class ConfigManager {
    private static volatile ConfigManager instance;
    private Properties configs = new Properties();

    private ConfigManager() {
        // 加载配置文件
        try (InputStream input = getClass().getResourceAsStream("app.properties")) {
            configs.load(input);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static ConfigManager getInstance() {
        if (instance == null) {
            synchronized (ConfigManager.class) {
                if (instance == null) {
                    instance = new ConfigManager();
                }
            }
        }
        return instance;
    }

    public String getConfig(String key) {
        return configs.getProperty(key);
    }
}
场景2:日志记录器
  • 需求:统一管理日志输出,避免多个日志实例竞争文件句柄。
  • 实现:通过单例模式封装日志工具(如Log4j2的LoggerContext)。
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import java.util.Objects;

public class LogSingleton {
    // 1. 静态私有实例 + volatile保证可见性
    private static volatile LogSingleton instance;
    // 2. Log4j2的LoggerContext(核心日志上下文)
    private LoggerContext loggerContext;
    // 3. 实际日志记录工具
    private Logger logger;

    // 4. 私有构造函数(初始化日志配置)
    private LogSingleton() {
        // 加载Log4j2配置(默认加载类路径下的log4j2.xml)
        loggerContext = (LoggerContext) LogManager.getContext(false);
        logger = loggerContext.getLogger(LogSingleton.class.getName());
        logger.info("LogSingleton初始化完成");
    }

    // 5. 双重检查锁获取单例
    public static LogSingleton getInstance() {
        if (instance == null) {
            synchronized (LogSingleton.class) {
                if (instance == null) {
                    instance = new LogSingleton();
                }
            }
        }
        return instance;
    }

    // 6. 提供日志记录方法(委托给Log4j2的Logger)
    public void info(String message) {
        logger.info(message);
    }

    public void error(String message, Throwable throwable) {
        logger.error(message, throwable);
    }

    // 7. 可选:重新加载配置文件(例如动态修改日志级别)
    public void reloadConfiguration() {
        if (loggerContext != null) {
            loggerContext.setConfigLocation(Objects.requireNonNull(
                getClass().getClassLoader().getResource("log4j2.xml")).toURI());
            logger.info("日志配置已重新加载");
        }
    }
}

类和对象的关系(UML类图)

+------------------+
|   Singleton      |
+------------------+
| - instance: Singleton |
+------------------+
| - Singleton()     |
| + getInstance(): Singleton |
+------------------+
  • Singleton类:包含一个静态私有变量instance,私有构造方法,公有静态方法getInstance()
  • 对象关系:通过getInstance()方法控制唯一实例的创建和访问。

我的总结

单例模式(singleton)

目的:使得全局只有一个实例

应用场景:读取配置、打印日志等功能类(需要全局唯一实例)

实现 - 类和对象的关系(UML类图):

+--------------------------------------------------------+
|   Singleton                                            |
+--------------------------------------------------------+
| - private static volatile instance: Singleton          |
+--------------------------------------------------------+
| - private xx:int  #属性1                               |
| - private xx:int  #属性2                               |
+--------------------------------------------------------+
| - Singleton()  #构造器:给普通属性赋值                    |
| + getInstance(): Singleton   #调用构造器,给静态属性赋值  |
+--------------------------------------------------------+
| + method1(): Singleton                                 |
| + method2(): int                                       |
+--------------------------------------------------------+
  • 对象关系:通过getInstance()方法控制唯一实例的创建和访问。(线程安全:双重检查 + synchronized 关键字)

适用性:单例模式适合需要全局唯一对象且频繁访问的场景。

注意事项:

  • 多线程环境下必须保证线程安全。
  • 反射和序列化可能破坏单例,需额外防护(如readResolve()方法)。
  • 过度使用单例可能导致代码耦合度高,测试困难。

    补充:

    上述单例模式也就是所谓的懒汉式,即在第一次调用getInstance()时才进行实例化

    所谓的饿汉式则是在类初始化时,就已经完成实例化,具体如下

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值