~/app/articles/NORMAL

cat article/43.md

单例模式 (Singleton) 全面讲解

学习笔记
设计模式
单例模式
Java
线程安全
编程实践

本文详细介绍了单例模式的概念、实现方式及其在多线程环境下的安全性问题,包括懒汉式和线程安全版的实现代码。

单例模式 (Singleton) 全面讲解

1. 什么是单例模式?

单例模式确保一个类只有一个实例,并提供一个全局访问点。

  • 为什么需要?
    比如数据库连接池、日志管理器、配置加载等场景,如果重复创建实例会浪费资源或导致数据不一致。

2. 单例模式的核心实现

关键点:

  1. 私有构造函数:防止外部通过new创建实例。
  2. 静态私有变量:保存唯一实例。
  3. 静态公有方法:提供全局访问点(如getInstance())。

2.1 基础版(懒汉式,线程不安全)

public class Singleton {
    private static Singleton instance;  // 静态私有变量
    
    private Singleton() {}  // 私有构造函数
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();  // 延迟初始化
        }
        return instance;
    }
}
  • 问题:多线程环境下可能创建多个实例(考试常考!)。

2.2 线程安全版(加锁)

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static synchronized Singleton getInstance() {  // 加锁
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 缺点:每次调用getInstance()都加锁,性能差。

2.3 双重检查锁(DCL,最优解)

public class Singleton {
    private static volatile Singleton instance;  // volatile防止指令重排序
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                     // 第一次检查
            synchronized (Singleton.class) {         // 加锁
                if (instance == null) {             // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 为什么用volatile?
    防止JVM指令重排序导致未初始化的对象被返回(进阶考点)。

2.4 饿汉式(线程安全,推荐)

public class Singleton {
    private static final Singleton instance = new Singleton();  // 类加载时初始化
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    }
}
  • 优点:简单、线程安全(利用类加载机制)。
  • 缺点:即使不用也会创建实例,可能浪费内存。

3. 应用场景

  • 经典用途:
    • 数据库连接池(如DataSource)。
    • 日志管理器(全局共享日志文件)。
    • 配置信息类(如AppConfig)。
  • 面试考点:
    • 如何破坏单例?(反射、序列化)
    • 如何防御?(枚举单例、readResolve()方法)

4. 互动实践

问题1:以下代码是单例吗?为什么?

public class FakeSingleton {
    public static FakeSingleton instance = new FakeSingleton();
    private FakeSingleton() {}
}
  • 答案:不是!因为instance是public的,外部可直接修改,如FakeSingleton.instance = null;。

问题2:如何用枚举实现单例?

public enum EnumSingleton {  // 枚举天然防反射、序列化破坏
    INSTANCE;
    public void doSomething() {
        System.out.println("Singleton method");
    }
}
  • 调用方式:EnumSingleton.INSTANCE.doSomething();
  • 优点:最简单、最安全的单例实现(Joshua Bloch推荐)。

5. 考试练习题

题目1(基础)

单例模式的目的是什么?
A. 限制类的实例化次数
B. 提高类的性能
C. 允许子类覆盖父类方法
D. 实现多态

答案:A


题目2(代码填空)

补全以下双重检查锁单例模式的代码:

public class Singleton {
    private static ____ Singleton instance;  // 关键字填空
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            ____ (Singleton.class) {
                if (instance == null) {
                    instance = ____ Singleton();
                }
            }
        }
        return instance;
    }
}

答案:

  1. volatile
  2. synchronized
  3. new

题目3(进阶)

为什么双重检查锁需要volatile关键字?
答案:防止JVM指令重排序导致其他线程获取到未初始化的对象。


$ comments --list (0)

// not signed in


// no comments yet