【设计模式】单例模式

文章目录

  • 【设计模式】单例模式
    • 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();
  • 优点:绝对单例(防止反射/序列化破坏)

  • 缺点:不够灵活

各实现方式对比

实现方式 延迟加载 线程安全 性能 防反射 防序列化
饿汉式
懒汉式
同步方法懒汉式 低(频繁加锁)
双重检查锁定 较高
静态内部类
低(频繁加锁)
双重检查锁定 较高
静态内部类
枚举
相关推荐
书源丶4 分钟前
三十九、Java 枚举——固定常量的「安全卫士」
java·开发语言
逝水如流年轻往返染尘16 分钟前
设计模式之单例模式
单例模式·设计模式
seven97_top25 分钟前
Tomcat 线程池的设计与实现:StandardThreadExecutor
java·tomcat
Javatutouhouduan33 分钟前
大厂Java岗最新面试真题汇总!
java·java面试·后端开发·java编程·java程序员·互联网大厂·java八股文
逻辑驱动的ken33 分钟前
Java高频面试考点场景题23
java·开发语言·数据库·面试·职场和发展·哈希算法
xxjj998a41 分钟前
PHP vs Java:核心区别与应用场景全解析
java·开发语言·php
用户298698530141 小时前
Java 从零生成 Word 文档:段落、图片与表格操作
java·后端
2401_833269301 小时前
Java IO流:从字节到字符的桥梁
java·开发语言
月落归舟1 小时前
深入剖析乐观锁背后的原理
java·乐观锁
SimonKing1 小时前
OpenCode 在 IDEA 中使用 ACP 协议 VS 直接使用 TUI,哪个编程方式更是你的菜?
java·后端·程序员