上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

15.单例模式

guduadmin44小时前

文章目录

  • 单例模式
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 饿汉式(线程安全)
  • 双重检查锁定(Double-Checked Locking)
  • 静态内部类
  • 枚举

    单例模式

    单例模式(Singleton Pattern) 是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这样的实例通常用于控制对资源的访问,例如数据库连接、线程池、日志对象等。

    在单例模式中,类负责创建自己的唯一实例,并提供一个静态方法让外部代码访问该实例。

    下面我们来实现单例模式。

    懒汉式(线程不安全)

    懒汉式是指在第一次调用 getInstance 方法时才会创建实例。这种实现方式在多线程环境下是不安全的。

    public class LazySingleton {
        private static LazySingleton instance;
        private LazySingleton() {}
        public static LazySingleton getInstance() {
            if (instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }
    

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

    在懒汉式的基础上,通过添加 synchronized 关键字来保证线程安全。但这种方式会带来性能问题,因为每次获取实例都要进行同步。

    public class SynchronizedLazySingleton {
        private static SynchronizedLazySingleton instance;
        private SynchronizedLazySingleton() {}
        public static synchronized SynchronizedLazySingleton getInstance() {
            if (instance == null) {
                instance = new SynchronizedLazySingleton();
            }
            return instance;
        }
    }
    

    饿汉式(线程安全)

    饿汉式是指在类加载的时候就创建实例,因此不存在多线程环境下的线程安全问题。但在类加载时就创建实例,可能造成资源浪费。

    public class EagerSingleton {
        private static final EagerSingleton instance = new EagerSingleton();
        private EagerSingleton() {}
        public static EagerSingleton getInstance() {
            return instance;
        }
    }
    

    双重检查锁定(Double-Checked Locking)

    通过双重检查锁定机制,在懒汉式的基础上进行优化,提高了性能。

    public class DoubleCheckedLockingSingleton {
        private static volatile DoubleCheckedLockingSingleton instance;
        private DoubleCheckedLockingSingleton() {}
        public static DoubleCheckedLockingSingleton getInstance() {
            if (instance == null) {  // 第一次检查
                synchronized (DoubleCheckedLockingSingleton.class) {
                    if (instance == null) {  // 第二次检查
                        instance = new DoubleCheckedLockingSingleton();
                    }
                }
            }
            return instance;
        }
    }
    
    • 第一次检查: 主要是为了避免不必要的同步开销。当实例已经创建后,不再需要进入同步块,直接返回已创建的实例。

    • 第二次检查: 在同步块内进行创建实例的操作,如果没有第二次检查,可能会导致多个线程都通过第一次检查,进入同步块,然后其中一个线程创建了实例,而其他线程没有再次检查,也会再次创建实例,破坏了单例的原则。

    • 使用 volatile 关键字修饰 instance 变量,确保多线程环境下的可见性。在 Java 5 及以上的版本,volatile 关键字的语义已经有了明确的规定,可以保证双重检查锁定在多线程环境下的正确性。

      静态内部类

      通过静态内部类的方式实现单例模式,利用了类加载的机制,保证了线程安全。

      public class StaticInnerClassSingleton {
          private StaticInnerClassSingleton() {}
          private static class SingletonHolder {
              private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
          }
          public static StaticInnerClassSingleton getInstance() {
              return SingletonHolder.instance;
          }
      }
      
      • 类加载时的线程安全性: 类加载过程中,当类被加载到内存中时,类初始化阶段是由 JVM 负责的,它保证了在多线程环境下,一个类只会被初始化一次。这是通过类加载器的锁机制来实现的。

      • 静态内部类的延迟加载: 静态内部类不会在外部类加载时立即被加载,而是在第一次使用时才被加载。由于类加载和初始化是线程安全的,所以静态内部类的加载也是线程安全的。

        类加载机制

        在 Java 中,类加载器(ClassLoader)是负责加载 Java 类的机制。类加载器的主要任务是将类的字节码加载到内存中,并转换为一个 Class 类型的对象。类加载器通过委托模型(Delegation Model)来实现对类的加载,同时采用了一些机制确保类只被初始化一次。

        类加载器的工作原理:

        1. 加载(Loading): 类加载器负责将类的字节码加载到内存中。

        2. 链接(Linking): 链接阶段包括验证、准备和解析。其中,验证是确保字节码符合 Java 虚拟机规范,准备是为类的静态变量分配内存并设置默认初始值,解析是将符号引用转换为直接引用。

        3. 初始化(Initialization): 在类初始化阶段,执行类的初始化代码。这个阶段是延迟进行的,即在对类的第一次主动使用时才会触发。

        为什么类加载器能够实现一个类只被初始化一次:

        1. 委派模型: Java 的类加载器采用了委派模型,即每个类加载请求都会先委派给父类加载器处理。这样可以保证同一个类不会被多个类加载器重复加载。

        2. 类初始化锁: 在类初始化阶段,类加载器会使用一个互斥锁,确保只有一个线程能够初始化该类。这样可以避免多个线程同时初始化同一个类,保证线程安全性。

        3. 初始化标记: 类加载器在完成对类的初始化后,会在内部使用一个标记,表明该类已经被初始化过。当其他类加载器尝试再次加载同一个类时,会检查这个标记,避免重复初始化。

        枚举

        枚举方式是 JDK 1.5 之后引入的,它天生就是线程安全的,而且可以防止反射和反序列化攻击。

        public enum EnumSingleton {
            INSTANCE;
            // 可以添加其他方法和属性
        }
        

网友评论

搜索
最新文章
热门文章
热门标签