单例模式实现线程安全双重检查锁定Double-Checked Locking它是一种常见而有效的写作方法。它不仅保证了性能而且保证了多线程环境中唯一的例子。单例模式的基本要求单例模式的核心是一个类别只允许创建一个例子并提供一个全球访问点。在多线程场景中必须防止多线程同时创建多个例子。常见的实现方式包括饿汉风格类加载初始化线程安全但可能浪费资源懒汉风格只有在第一次使用时才能创建线程安全问题需要处理双重检查锁延迟加载 高效的线程安全实现代码的双重检查锁定public class Singleton { // 使用 volatile 关键字确保多线程内存的可见性并禁止指令重新排序 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; } }双检锁定原理分析这种写作方法之所以有效是因为它降低了同步成本。只有在实例没有创建时才会锁定。一旦创建完成后续调用将直接返回引用无需同步。第一次检查 instance null如果存在实例直接返回不进入同步块提高性能synchronized 确保只有一个线程能同时进入临界区第二次检查 instance null防止多个线程在第一次检查后进入同步块导致重复创建volatile 防止对象创建过程中的指令重新排序new Singleton() 不是原子操作大致分为三个步骤:内存空间的分配调用构造函数的初始对象将 instance 指向内存地址由于 JVM 对于处理器的优化可能会对指令进行重新排序如执行第一步和第三步然后执行第二步。此时如果此时正好读取另一个线程 instance会发现它非 null但对象还没有完全初始化导致错误。volatile 这种重排序可以被禁止以确保对象在结构完成前不会被其他线程看到。为什么不加 volatile 会有问题在没有 volatile 在修改过程中线程A在创建对象时进行了重新排序线程B可能会被“半初始化”对象引用并且在调用其方法时会出现不可预测的行为。此外 volatile 后JVM 将内存屏障插入其他线程以确保正确的结构顺序 always 是完整构造后的例子。
Java线程安全的单例模式如何实现 双重检查锁定原理
单例模式实现线程安全双重检查锁定Double-Checked Locking它是一种常见而有效的写作方法。它不仅保证了性能而且保证了多线程环境中唯一的例子。单例模式的基本要求单例模式的核心是一个类别只允许创建一个例子并提供一个全球访问点。在多线程场景中必须防止多线程同时创建多个例子。常见的实现方式包括饿汉风格类加载初始化线程安全但可能浪费资源懒汉风格只有在第一次使用时才能创建线程安全问题需要处理双重检查锁延迟加载 高效的线程安全实现代码的双重检查锁定public class Singleton { // 使用 volatile 关键字确保多线程内存的可见性并禁止指令重新排序 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; } }双检锁定原理分析这种写作方法之所以有效是因为它降低了同步成本。只有在实例没有创建时才会锁定。一旦创建完成后续调用将直接返回引用无需同步。第一次检查 instance null如果存在实例直接返回不进入同步块提高性能synchronized 确保只有一个线程能同时进入临界区第二次检查 instance null防止多个线程在第一次检查后进入同步块导致重复创建volatile 防止对象创建过程中的指令重新排序new Singleton() 不是原子操作大致分为三个步骤:内存空间的分配调用构造函数的初始对象将 instance 指向内存地址由于 JVM 对于处理器的优化可能会对指令进行重新排序如执行第一步和第三步然后执行第二步。此时如果此时正好读取另一个线程 instance会发现它非 null但对象还没有完全初始化导致错误。volatile 这种重排序可以被禁止以确保对象在结构完成前不会被其他线程看到。为什么不加 volatile 会有问题在没有 volatile 在修改过程中线程A在创建对象时进行了重新排序线程B可能会被“半初始化”对象引用并且在调用其方法时会出现不可预测的行为。此外 volatile 后JVM 将内存屏障插入其他线程以确保正确的结构顺序 always 是完整构造后的例子。