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 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。