文章目录
- 单例模式
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 饿汉式(线程安全)
- 双重检查锁定(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)来实现对类的加载,同时采用了一些机制确保类只被初始化一次。
类加载器的工作原理:
-
加载(Loading): 类加载器负责将类的字节码加载到内存中。
-
链接(Linking): 链接阶段包括验证、准备和解析。其中,验证是确保字节码符合 Java 虚拟机规范,准备是为类的静态变量分配内存并设置默认初始值,解析是将符号引用转换为直接引用。
-
初始化(Initialization): 在类初始化阶段,执行类的初始化代码。这个阶段是延迟进行的,即在对类的第一次主动使用时才会触发。
为什么类加载器能够实现一个类只被初始化一次:
-
委派模型: Java 的类加载器采用了委派模型,即每个类加载请求都会先委派给父类加载器处理。这样可以保证同一个类不会被多个类加载器重复加载。
-
类初始化锁: 在类初始化阶段,类加载器会使用一个互斥锁,确保只有一个线程能够初始化该类。这样可以避免多个线程同时初始化同一个类,保证线程安全性。
-
初始化标记: 类加载器在完成对类的初始化后,会在内部使用一个标记,表明该类已经被初始化过。当其他类加载器尝试再次加载同一个类时,会检查这个标记,避免重复初始化。
枚举
枚举方式是 JDK 1.5 之后引入的,它天生就是线程安全的,而且可以防止反射和反序列化攻击。
public enum EnumSingleton { INSTANCE; // 可以添加其他方法和属性 }
-
-
-
猜你喜欢
- 4小时前QT基础篇(7)QT5图形视图框架
- 4小时前vue中PC端使用高德地图 -- 实现搜索定位、地址标记、弹窗显示定位详情
- 4小时前Vue项目中关于全局css的处理
- 4小时前基于Java Web的图书管理系统的设计与实现
- 4小时前前端大屏适配几种方案
- 4小时前HTTPS:如何确保您的网站数据传输安全?
- 4小时前Kafka+redis分布式锁结合使用心得总结
- 4小时前JavaMySql+hadoop高校固定资产管理系统 74965(免费领源码)计算机毕业设计选题推荐上万套实战教程JAVA、PHP,node.js,C++、python等
- 4小时前Java 栈和队列的交互实现
- 2小时前空调模式图标含义图解(格力空调模式图标含义图解)
网友评论
- 搜索
- 最新文章
- 热门文章