单例模式介绍

单例模式大家都很清楚,最常见的是饿汉式与懒汉式。也有不常见的静态内部类式与枚举式以及,懒汉式的线程安全版本。

单例模式常被用于全局式的配置管理(数据库连接池,日志管理器),资源共享(线程池,缓存控制器),状态管理(程序计数器)等。

一. 单例模式的实现方式

1,饿汉式单例模式

特点:线程安全,但在类加载时就创建实例,不管是否使用都会占用资源

java 复制代码
/**
 * 饿汉式单例模式
 * 特点:线程安全,但在类加载时就创建实例,不管是否使用都会占用资源
 */
public class SingletonEager {
    // 在类加载时就创建实例
    private static final SingletonEager instance = new SingletonEager();
    
    // 私有构造函数,防止外部实例化
    private SingletonEager() {}
    
    // 提供全局访问点
    public static SingletonEager getInstance() {
        return instance;
    }
}

2, 懒汉式单例模式(线程不安全版本)

特点:延迟加载,但在多线程环境下可能创建多个实例

java 复制代码
/**
 * 懒汉式单例模式(线程不安全版本)
 * 特点:延迟加载,但在多线程环境下可能创建多个实例
 */
public class SingletonLazy {
    // 声明实例,但不创建
    private static SingletonLazy instance;
    
    // 私有构造函数
    private SingletonLazy() {}
    
    // 提供全局访问点,在首次调用时创建实例
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

3,线程安全的懒汉式单例模式

特点:延迟加载且线程安全,但同步方法影响性能

java 复制代码
public class SingletonLazySafe {
    private static SingletonLazySafe instance;
    
    private SingletonLazySafe() {}
    
    // 使用synchronized关键字确保线程安全
    public static synchronized SingletonLazySafe getInstance() {
        if (instance == null) {
            instance = new SingletonLazySafe();
        }
        return instance;
    }
}

4,双重检查锁定的单例模式

特点:延迟加载,线程安全,且大部分情况下避免了同步锁的性能影响

java 复制代码
/**
 * 双重检查锁定的单例模式
 * 特点:延迟加载,线程安全,且大部分情况下避免了同步锁的性能影响
 */
public class SingletonDoubleCheck {
    // volatile关键字确保多线程环境下的可见性
    private static volatile SingletonDoubleCheck instance;
    
    private SingletonDoubleCheck() {}
    
    public static SingletonDoubleCheck getInstance() {
        // 第一次检查
        if (instance == null) {
            // 同步锁
            synchronized (SingletonDoubleCheck.class) {
                // 第二次检查
                if (instance == null) {
                    instance = new SingletonDoubleCheck();
                }
            }
        }
        return instance;
    }
}

5,静态内部类单例模式

特点:延迟加载,线程安全,且不需要同步

java 复制代码
/**
 * 静态内部类单例模式
 * 特点:延迟加载,线程安全,且不需要同步
 */
public class SingletonStaticInner {
    
    private SingletonStaticInner() {}
    
    // 静态内部类,只有在首次调用getInstance()时才会被加载
    private static class SingletonHolder {
        private static final SingletonStaticInner INSTANCE = new SingletonStaticInner();
    }
    
    public static SingletonStaticInner getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
     

6, 枚举单例模式

特点:最简洁的实现,自动支持序列化,绝对防止多次实例化

java 复制代码
/**
 * 枚举单例模式
 * 特点:最简洁的实现,自动支持序列化,绝对防止多次实例化
 */
public enum SingletonEnum {
    INSTANCE;
    
    // 业务方法示例
    public int findLongestPalindrome(String s) {
        if (s == null || s.length() < 1) return 0;
        
        int maxLength = 0;
        for (int i = 0; i < s.length(); i++) {
            // 处理奇数长度的回文串
            int len1 = expandAroundCenter(s, i, i);
            // 处理偶数长度的回文串
            int len2 = expandAroundCenter(s, i, i + 1);
            
            maxLength = Math.max(maxLength, Math.max(len1, len2));
        }
        
        return maxLength;
    }
    
    private int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}

二. 那么单例模式与单例bean的区别在哪呢?为什么选用单例模式,不用单例bean?

我们先来回顾一下单例bean,一般来说bean是指的是Spring容器的管理对象。由IOC创建并管理,其生命周期伴随着Spring容器。

而单例模式,本身是一种设计模式,一种编程的概念。

先看看具体有哪些不同。

1,实现方式

单例模式:需要开发者手动创建和管理

单例bean:由Spring框架自动管理

2,作用域

单例模式:项目整体

单例bean:Spring IOC容器

3,线程安全

单例模式:需要开发者手动实现

单例bean:默认线程不安全,如有成员变量,需开发者手动使用同步锁维护线程安全

4,生命周期

单例模式:类加载器控制,通常伴随程序的整个生命周期

单例bean:由容器管理

5,应用场景区别

单例模式:全局配置管理(如数据库连接池),工具类(如日志管理器),硬件访问控制(如打印机服务)。

单例bean:需要Spring管理的各种层级对象,缓存与事务管理器等配置

三. 单例模式的类一定只会有一个对象实例么?

我们首先参考作用域,单例模式一般是伴随整个程序。这是基于单个jvm的情况。在分布式环境下相当于每个jvm都有一个相同的单例对象,也就是全局范围内多个对象。

而在单个jvm中也是有可能出现单例被破坏的情况的。最简单的就是利用反射。

1,反射攻击

即使单例类的构造函数被声明为 private,通过反射仍可获取构造方法并设置其可访问性为 true,从而强制创建新实例。

java 复制代码
Class<?> clazz = Singleton.class;
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);  // 绕过访问控制
Singleton newInstance = (Singleton) constructor.newInstance();

2,序列化与反序列化

如果单例类实现了 Serializable 接口,反序列化时会创建新的实例。

3,有多个类加载器加载同一单例对象

不同类加载器加载同一个类会生成多个实例。

4,多线程未同步的懒汉式

在多线程高并发环境下,可能会创建出多个实例。

5,分布式系统或集群环境

在分布式系统中,每个 JVM 进程会维护自己的单例实例,导致全局范围内存在多个实例。

相关推荐
不当菜虚困4 小时前
JAVA设计模式——(八)单例模式
java·单例模式·设计模式
北漂老男孩7 小时前
设计模式全解析:23种经典设计模式及其应用
单例模式·设计模式
冼紫菜16 小时前
SpringBoot配置RestTemplate并理解单例模式详解
java·spring boot·后端·spring·单例模式
半青年19 小时前
单例模式:全局唯一性在软件设计中的艺术实践
java·c++·python·单例模式
Java致死1 天前
单例设计模式
java·单例模式·设计模式
zyx没烦恼3 天前
线程池&&单例模式
linux·开发语言·c++·单例模式
碎梦归途5 天前
23种设计模式-结构型模式之适配器模式(Java版本)
java·开发语言·jvm·单例模式·设计模式·适配器模式
CHQIUU5 天前
Java 设计模式心法之第4篇 - 单例 (Singleton) 的正确打开方式与避坑指南
java·单例模式·设计模式
嘵奇6 天前
Java单例模式详解:实现线程安全的全局访问点
java·安全·单例模式