设计模式之-单例设计模式

一、单例模式定义

单例设计模式(Singleton Pattern)是一种常用的创建型设计模式,其目的是 确保一个类在系统中只有一个实例,并提供一个全局访问点来获取这个唯一实例。
它常用于管理共享资源,如线程池、数据库连接池、配置对象、日志对象等。

  1. 只有一个实例:类在系统中只能有一个实例存在。
  2. 自行实例化:类自己创建并持有这个实例。
  3. 对外提供访问点:通过一个静态方法让外部获取这个唯一实例。

二、单例模式的结构

类图如下:

+-----------------+
|   Singleton     |
+-----------------+
| - instance      |  静态私有变量,保存唯一实例
| + getInstance() |  静态公有方法,返回实例
+-----------------+

三、实现方式

1. 饿汉式(线程安全,类加载时创建实例)

public class Singleton {
    // 在类加载时就初始化实例
    //在类加载时就会创建该实例,因为 Java 类加载是线程安全的,且是在类加载阶段就完成初始化的,因此不需要同步机制
    private static final Singleton instance = new Singleton();

    // 私有构造器,防止外部直接实例化
    private Singleton() {}

    // 提供公共的获取实例的方法
    public static Singleton getInstance() {
        return instance;
    }
}

优点: 简单、线程安全。
缺点: 类加载时就创建实例,可能浪费资源(如果没用到的话)。


2. 懒汉式(延迟加载,非线程安全)

/**
 * 懒汉式单例模式的实现方式与饿汉式单例不同,它不在类加载时就创建实例,而是在第一次访问时才创建。
 * 懒汉式单例模式(Lazy Initialization Singleton)是单例模式的一种实现方式,它延迟实例化对象,直到需要使用该对象时才进行创建。
 * 这种方式适用于那些在应用启动时不确定是否需要某个对象的场景。
 * 懒汉模式的关键点是 实例化延迟,即 第一次使用时才创建实例。
 *
 * 存在问题:
 * 当多个线程同时调用 getInstance() 方法时,可能会创建多个实例。
 * 为了保证线程安全,可以使用 synchronized 关键字来加锁,确保在多线程环境下只有一个线程能访问实例化过程。
 *
 */
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();  // 多线程下可能创建多个实例
        }
        return instance;
    }
}

优点: 延迟创建实例,节省资源。
缺点: 多线程下不安全。


3. 懒汉式(线程安全,加锁)

public class Singleton {
    // 使用 volatile 保证实例的可见性
    private static volatile Singleton instance;

    // 私有构造器,防止外部直接实例化
    private Singleton() {}

    // 使用 synchronized 确保线程安全
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();  // 延迟实例化
                }
            }
        }
        return instance;
    }
}

缺点: 每次调用都要加锁,性能较差。


4. 双重检查锁(DCL,推荐方式)

public class Singleton {
    // 使用 volatile 保证实例的可见性
    // 即一个线程修改了实例变量,其他线程能够立刻看到修改后的值。
    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;
    }
}

优点: 线程安全、延迟加载、效率高。


5. 静态内部类(推荐方式)

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.instance;
    }
}

优点: 利用类加载机制保证线程安全和延迟初始化,无需加锁,效率高。


四、适用场景

  • 配置对象管理(如全局配置读取器)
  • 资源控制器(如数据库连接池)
  • 日志工具类
  • 操作系统文件系统、窗口管理器等只有一个实例的系统服务

五、优缺点总结

优点

缺点

控制实例数量,节省资源

不易扩展,不利于单元测试

全局访问点,便于管理

多线程实现复杂(尤其懒加载)

避免频繁创建销毁对象,提高效率

违背面向对象的开放-封闭原则(难以继承)

六、使用示例

假设我们有一个 日志管理系统,它需要使用单例模式来确保只有一个日志实例,

并通过代理模式来控制对日志写入的访问(例如添加额外的日志验证、权限检查等)。

public interface Logger {
    void log(String message);
}
public class RealLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("日志记录: " + message);
    }
}
public class LoggerSingleton {
    // 使用饿汉式单例,实例在类加载时创建
    private static final RealLogger instance = new RealLogger();

    private LoggerSingleton() {
        // 私有化构造器,防止外部创建实例
    }

    public static RealLogger getInstance() {
        return instance;
    }
}
public class LoggerProxy implements Logger {
    private final RealLogger realLogger;

    public LoggerProxy() {
        // 获取单例对象
        this.realLogger = LoggerSingleton.getInstance();
    }

    @Override
    public void log(String message) {
        // 在执行真实日志写入之前,添加一些额外的操作
        checkPermissions();
        formatMessage(message);

        // 调用真实日志写入
        realLogger.log(message);
    }

    private void checkPermissions() {
        // 模拟权限检查
        System.out.println("检查日志写入权限...");
        // 假设权限检查通过
        System.out.println("权限验证通过。");
    }

    private void formatMessage(String message) {
        // 模拟格式化日志
        System.out.println("格式化日志信息...");
    }
}

public class Client {
    public static void main(String[] args) {
        // 使用代理来记录日志
        Logger logger = new LoggerProxy();

        // 记录日志
        logger.log("这是一个代理模式和单例模式的示例!");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码蚁Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值