🗒️单例模式的代码实现
2024-10-5
| 2024-10-31
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
创建时间
Oct 31, 2024 01:49 PM
要实现一个单例,我们需要关注的点无外乎下面几个:
  • 构造函数需要是 private 访问权限的,这样才能避免外部通过 new 创建实例;
  • 考虑对象创建时的线程安全问题;
  • 考虑是否支持延迟加载;
  • 考虑 getInstance() 性能是否高(是否加锁)

饿汉式

在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。

懒汉式

不安全的懒汉式

安全的懒汉式

我们给 getInstance()​ 这个方法加了一把大锁(synchronzed​),导致这个函数的并发度很低。量化一下的话,并发度是 1,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调用。
​synchronized​ 锁临界资源=多线程同时会竞争的资源。锁的粒度太大了。

双重检测的懒汉式

CPU 指令重排序可能导致在 IdGenerator 类的对象被关键字 new 创建并赋值给 instance 之后,还没来得及初始化(执行构造函数中的代码逻辑),就被另一个线程使用了。这样,另一个线程就使用了一个没有完整初始化的 IdGenerator 类的对象。要解决这个问题,我们只需要给 instance 成员变量添加 volatile 关键字来禁止指令重排序即可。

静态内部类

该代码通过使用静态内部类 SingletonHolder 来实现延迟加载(Lazy Initialization)。以下是如何做到这一点的详细解释:
静态内部类: SingletonHolder 是一个私有的静态内部类,它持有一个 IdGenerator 的实例。这个实例是在 SingletonHolder 类被加载时创建的。
延迟加载机制: Java 中,类的静态成员变量是在其所属类第一次被访问时才会初始化。这意味着,直到有人调用 getInstance() 方法之前,SingletonHolder 类不会被加载,也就不会创建 IdGenerator 实例。当第一次调用 getInstance() 时,会触发对 SingletonHolder.instance 的访问,此时 JVM 会去加载并初始化 SingletonHolder 类,从而创建唯一的单例实例。
线程安全性:静态内部类方式不仅实现了懒汉式单例模式,而且由于 JVM 在加载和初始化类的时候具有线程安全性,因此不需要显式地进行同步控制,这样既保证了线程安全,又避免了性能损耗。

枚举式

通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。

📎 参考

 
HTTPS中的SSL/TLS握手过程Redis 数据结构
Loading...