【设计模式】单例模式

文章目录

  • 【设计模式】单例模式
    • 1、什么是单例模式
    • 2、单例模式实现
      • [🎍2.1 饿汉式(线程安全)](#🎍2.1 饿汉式(线程安全))
      • [🍔2.2 懒汉式(线程不安全)](#🍔2.2 懒汉式(线程不安全))
      • [🍪2.3 同步方法懒汉式 (线程安全)](#🍪2.3 同步方法懒汉式 (线程安全))
      • [🏇2.4 双重检查锁定](#🏇2.4 双重检查锁定)
      • [🚗2.5 静态内部类(线程安全)](#🚗2.5 静态内部类(线程安全))
      • [🎠2.6 枚举单例](#🎠2.6 枚举单例)
      • 各实现方式对比

【设计模式】单例模式

1、什么是单例模式

单例模式(Singleton): 保证整个系统中一个类仅有一个对象实例,并提供一个可以访问它的全局访问点。

  • 为什么要设计单例模式?单例模式的优点体现在哪些方面?

    单例模式的设计保证了一个类在整个系统中同一时刻只有一个实例存在,主要被用于一个全局类的对象在多个地方被使用并且对象的状态是全局变化的场景下。同时,单例模式为系统资源的优化提供了很好的思路,频繁创建和销毁对象都会增加系统的资源消耗,而单例模式保障了整个系统只有一个对象能被使用,很好地节约了资源。

2、单例模式实现

🎍2.1 饿汉式(线程安全)

**原理:**在类加载时就创建实例

java 复制代码
 //饿汉式
 public class HungrySingleton {
     private static HungrySingleton instance = new HungrySingleton();
     private HungrySingleton(){}
     public static HungrySingleton getInstance(){
         return instance;
     }
 }

类中直接定义全局的静态对象的实例并初始化,然后提供一个方法获取该实例对象。优点是实现简单,并且线程安全,缺点也很明显,比如在代码中没有使用这个对象也会创建出一个实例,可能会造成资源浪费。

🍔2.2 懒汉式(线程不安全)

**原理:**首次使用时才创建实例

java 复制代码
public class LazySingleton {
    private static LazySingleton instance;
    
    private LazySingleton() {}
    
    public static LazySingleton getInstance() {
        if (instance == null) {
            // 多线程可能同时进入此区域
            instance = new LazySingleton();
        }
        return instance;
    }
}

做法和饿汉式基本差不多,只是在返回实例对象时加入判断,如果已经创建,直接返回创建好的实例对象,确保首次使用才创建。不过需要注意的是,在进行判断时,多线程情况下的其他线程也可能会进入判断,导致线程不安全。优点是延迟加载,需要用到才会创建,一旦创建就一直使用已经创建好的实例,改善了饿汉式的缺点。

🍪2.3 同步方法懒汉式 (线程安全)

**原理:**通过方法级同步保证线程安全

java 复制代码
public class SyncMethodSingleton {
    private static SyncMethodSingleton instance;
    
    private SyncMethodSingleton() {}
    
    // 方法同步保证线程安全
    public static synchronized SyncMethodSingleton getInstance() {
        if (instance == null) {
            instance = new SyncMethodSingleton();
        }
        return instance;
    }
}

和懒汉式唯一的区别在于获取实例对象方法被synchronized 关键字修饰,相当于锁住整个类对象,同一时刻只允许一个线程进入方法,防止多线程情况下创建多个实例

  • 优点:实现简单且线程安全
  • 缺点:性能差(每次调用都同步)

🏇2.4 双重检查锁定

**原理:**减少同步范围 + volatile禁止指令重排序

java 复制代码
public class DCLSingleton {
    // volatile保证可见性和禁止指令重排序
    private static volatile DCLSingleton instance;
    
    private DCLSingleton() {}
    
    public static DCLSingleton getInstance() {
        if (instance == null) { // 第一次检查(无锁)
            synchronized (DCLSingleton.class) {
                if (instance == null) { // 第二次检查(有锁)
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

第一次if(instance==null):为了提高代码的执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不同进入同步代码块,不用竞争锁。

第二次if(instance==null):这个校验时防止二次创建实例。

  • 优点:高性能线程安全

  • 缺点:实现较复杂

1.为什么用 volatile
  • 禁止指令重排序

    instance = new Singleton()实际分为三步:

    1.分配内存空间

    2.初始化对象

    3.将引用指向内存地址

    若无 volatile,JVM 可能重排序为 1→3→2。此时其他线程可能拿到未初始化的对象(instance != null但对象不完整)。

  • 内存可见性 :确保所有线程看到最新的 instance值。

2.双重检查的必要性
  • 第一次检查:避免每次访问都加锁(性能优化)。
  • 第二次检查:防止多个线程同时通过第一次检查后重复创建实例。

🚗2.5 静态内部类(线程安全)

原理:利用类加载机制保证线程安全

java 复制代码
public class HolderSingleton {
    private HolderSingleton() {}
    
    // 静态内部类
    private static class SingletonHolder {
        private static final HolderSingleton INSTANCE = new HolderSingleton();
    }
    
    public static HolderSingleton getInstance() {
        // 首次调用时加载内部类
        return SingletonHolder.INSTANCE;
    }
}

静态内部类方式利用JVM类加载机制 确保线程安全,同时实现懒加载 特性(延迟初始化)。核心原理是:静态内部类不会在外部类加载时初始化,只有在显式调用时才会加载

  • 优点:延迟加载 + 无同步开销

  • 缺点:无法防止反射攻击

🎠2.6 枚举单例

**原理:**使用一个只包含单个元素的枚举类型来实现单例模式。

java 复制代码
public enum EnumSingleton {
    // 唯一实例
    INSTANCE;
    
    // 添加逻辑方法
    public void doSomething() {
        System.out.println("枚举单例方法");
    }
}

// 使用方式
EnumSingleton.INSTANCE.doSomething();
  • 优点:绝对单例(防止反射/序列化破坏)

  • 缺点:不够灵活

各实现方式对比

实现方式 延迟加载 线程安全 性能 防反射 防序列化
饿汉式
懒汉式
同步方法懒汉式 低(频繁加锁)
双重检查锁定 较高
静态内部类
低(频繁加锁)
双重检查锁定 较高
静态内部类
枚举
相关推荐
橙淮5 小时前
并发编程(六)
java·jvm
拽着尾巴的鱼儿5 小时前
springboot openfeign 自定义feign 接口重试机制
java·spring boot·后端
白露与泡影5 小时前
2026大厂Java面试题大全!牛客网最新版
java·开发语言
EntyIU6 小时前
JVM内存与GC笔记
java·jvm·笔记
XS0301066 小时前
并发编程 六
java·后端
yaoxin5211236 小时前
419. 现代 Java IO 最佳实践 - 写入文本文件
java·windows·python
雪宫街道6 小时前
synchronized 锁的范围:对象锁、类锁与代码块锁
java·jvm·后端·面试
x***r1516 小时前
linux安装 jdk-8u291-linux-x64.tar.gz 详细步骤(解压配置环境变量)
java
极光代码工作室7 小时前
基于SpringBoot的校园论坛系统
java·springboot·web开发·后端开发
XS0301067 小时前
Spring Bean 作用域 & 生命周期
java·后端·spring