Java 设计模式 二 单例模式 (Singleton Pattern)

单例模式 (Singleton Pattern) 是一种常见的设计模式,属于创建型模式。它的核心思想是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。通常用于那些需要全局控制的场景,比如配置管理、日志系统、数据库连接池等。

1. 单例模式的优点:

  • 全局访问点: 提供了一个全局唯一的实例,所有客户端都可以通过这个实例来访问相关功能。
  • 控制实例化次数: 确保只有一个实例,可以节省资源,并且避免对象的重复创建。
  • 延迟实例化: 只在需要时才创建实例,避免不必要的内存开销。

2. 单例模式的实现方式

1) 懒汉式(Lazy Initialization)

懒汉式是在第一次调用 getInstance() 方法时才创建实例,直到那时才初始化。为了保证线程安全,我们通常使用 synchronized 来同步 getInstance 方法。

优点: 延迟实例化,减少不必要的资源浪费。

缺点: 每次调用 getInstance() 时都要进行同步,性能较差。

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
2) 饿汉式(Eager Initialization)

饿汉式是在类加载时就创建实例,这种方式不需要进行同步,因此线程安全性较好。

优点: 实现简单,线程安全。

缺点: 无论是否使用该实例,类加载时就已经创建了对象,这可能会导致资源浪费。

java 复制代码
public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}
3) 双重检查锁定(Double-Checked Locking)

为了提高性能,可以在第一次检查时不加锁,只有在实例为 null 时才加锁。加锁的操作只会发生一次,从而避免每次调用时都进行同步。

优点: 性能较好,仅在第一次创建实例时加锁。

缺点: 代码复杂,且需要使用 volatile 关键字确保多线程情况下的正确性。

java 复制代码
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;
    }
}
4) 静态内部类(Bill Pugh Singleton)

这种方式利用了类加载的机制,保证了线程安全,并且实现了懒加载。它是单例模式的推荐实现方式。

优点: 简洁、线程安全、懒加载,性能优秀。

缺点: 没有明显的缺点。

java 复制代码
public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
5) 枚举式(Enum Singleton)

Java 提供的枚举类型本身就是单例的,它可以保证线程安全、避免反序列化以及类加载机制的优势。

优点: 线程安全、避免反射攻击、保证单例。

缺点: 实现略显复杂,但在现代 Java 开发中,这通常是最推荐的单例实现方式。

java 复制代码
public enum Singleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something...");
    }
}

使用时:

java 复制代码
Singleton.INSTANCE.doSomething();

3. 何时使用单例模式

  • 共享资源:例如数据库连接池、线程池、配置管理等,需要在整个应用中共享一个对象实例。
  • 全局控制:需要在系统中保证唯一的控制对象,例如日志系统。
  • 频繁创建销毁对象的场景:例如复杂对象的创建、管理较为耗费资源,可以使用单例来避免重复创建。

4. 注意事项

  • 线程安全:在多线程环境下,需要确保实例化过程是线程安全的。
  • 反射和反序列化攻击:单例类可以通过反射或反序列化破坏其唯一性,枚举单例可以避免这种情况。
  • 性能问题:使用懒汉式时,如果没有做适当优化,可能会在高并发情况下影响性能。

总结

单例模式是一种非常常见且有用的设计模式,能够确保类只有一个实例,并且提供全局访问点。在 Java 中,推荐使用静态内部类单例模式枚举单例模式,这两种方式在性能、线程安全性和代码简洁性上都非常优秀。

相关推荐
KATA~3 分钟前
解决MyBatis-Plus枚举映射错误:No enum constant问题
java·数据库·mybatis
xyliiiiiL18 分钟前
一文总结常见项目排查
java·服务器·数据库
shaoing20 分钟前
MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
java·开发语言·数据库
腥臭腐朽的日子熠熠生辉1 小时前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码2 小时前
Spring Task 定时任务
java·前端·spring
俏布斯2 小时前
算法日常记录
java·算法·leetcode
27669582922 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息2 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring