0. 概述
单例模式(Singleton Pattern) 是一种创建型设计模式,确保一个类有且只有 一个实例 ,并提供 一个全局访问点 来获取该单例实例。
维基百科:
在软件工程中,单例模式是一种软件设计模式,它限制一个类只能有一个实例。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
关键特点 :
单一实例 : 一个类只有一个实例。
全局访问点 : 提供一个全局访问点来获取该实例。
基本结构 :
私有构造函数 : 构造函数被设为私有,确保该类无法在类外部被实例化。
私有静态字段 : 一个私有静态字段 instance
用来保存该类的唯一实例,并且该实例应在类内部创建。
公共静态方法 : 一个公共静态方法 getInstance()
,通常作为全局访问点,用来返回该类的单例实例。
优点 :
只有一个单一实例。
提供一个全局可访问的单一访问点。
对于管理共享资源非常有用。
防止频繁创建和销毁实例,从而减少内存开销。
通过使用单一实例确保一致性。
基于延迟初始化(Lazy Initialization),实例可以在首次访问时创建。
缺点 :
不够灵活:它会使代码与特定实例紧密耦合,从而降低灵活性。
通常被标记为 final
,或者以不能被继承的方式设计。
不适用于可变对象。如果相同类型的对象在不同使用场景中需要发生变化,容易导致数据错误。
如果功能设计不合理,可能违反单一职责原则 。
使单元测试变得具有挑战性。
几种单例模式实现的比较 :
1. 懒汉式单例
懒加载意味着实例只会在首次访问或首次调用静态方法时创建。
1.1. 懒汉式单例 - 线程不安全
它在单线程环境下工作正常,但在多线程环境中可能会破坏单例模式,导致创建多个不同的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class LazySingletonThreadUnsafe { private static LazySingletonThreadUnsafe instance; private LazySingletonThreadUnsafe () { } public static LazySingletonThreadUnsafe getInstance () { if (null == instance) { instance = new LazySingletonThreadUnsafe (); } return instance; } }
测试代码:
1 2 3 4 5 6 7 8 9 @Test public void testLazySingletonThreadUnsafeSingleThread () { LazySingletonThreadUnsafe instance1 = LazySingletonThreadUnsafe.getInstance(); LazySingletonThreadUnsafe instance2 = LazySingletonThreadUnsafe.getInstance(); System.out.println("instance1: " + instance1); System.out.println("instance2: " + instance2); Assertions.assertSame(instance1, instance2); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void testLazySingletonThreadUnsafeMultiThread () throws ExecutionException, InterruptedException { final int THREAD_COUNT = 100 ; ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); Callable<LazySingletonThreadUnsafe> callableTask = () -> LazySingletonThreadUnsafe.getInstance(); Future<LazySingletonThreadUnsafe>[] futures = new Future [THREAD_COUNT]; for (int i = 0 ; i < THREAD_COUNT; i++) { futures[i] = executorService.submit(callableTask); } LazySingletonThreadUnsafe firstInstance = futures[0 ].get(); for (Future<LazySingletonThreadUnsafe> future : futures) { LazySingletonThreadUnsafe instance = future.get(); System.out.println(instance); Assertions.assertSame(firstInstance, instance); } executorService.shutdown(); }
1.2. 懒汉式单例 - synchronized
1.1. 懒汉式单例 - 线程不安全 不支持多线程场景。我们可以简单粗暴地在 getInstance()
方法上添加 synchronized
锁。虽然这种方法可以解决线程安全问题,但效率低下,因为它通过锁定静态方法来锁定类对象(类级别锁,而不是对象级别锁)。这意味着每次多个线程尝试通过 getInstance()
方法访问单例实例时,都会被锁定,只有一个线程能够访问,这大大影响了性能。实际上,这种方法只需在类实例化时执行一次,之后的所有请求都可以直接返回已经实例化的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class LazySingletonSynchronized { private static LazySingletonSynchronized instance; private LazySingletonSynchronized () { } public synchronized static LazySingletonSynchronized getInstance () { if (null == instance) { instance = new LazySingletonSynchronized (); } return instance; } }
1.3. 懒汉式单例 - 延迟锁
基于 1.2. 懒汉式单例 - synchronized ,我们可以考虑仅在第一次返回实例时锁定实例化过程。实例在临界区内被实例化,以确保多线程环境中的线程安全。之后的所有操作将直接返回该实例,而不进入临界区,以提高多线程性能。
但是,在多线程环境中仍然会存在线程安全问题。例如,在以下场景中,线程 T1 和 T2 可能会创建两个不同的实例。
T1:刚进入临界区,尚未实例化:instance=null
,T1 持有类级别锁
T2:进入 if 代码块,因为 T1 中的 instance
字段尚未实例化完成(instance
仍然为 null):instance=null
,T1 持有类级别锁,T2 在进入临界区前被阻塞
T1:完成实例化并退出临界区:instance=LazySingletonDelayLock@4a87761d
(instance!=null
),T1 释放锁,T2 有机会获得锁
T2:进入临界区并再次执行实例化:instance=LazySingletonDelayLock@2db7a79b
(现在它返回了两个不同的实例,破坏了单例模式!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class LazySingletonDelayLock { private static LazySingletonDelayLock instance; private LazySingletonDelayLock () { } public static LazySingletonDelayLock getInstance () { if (null == instance) { synchronized (LazySingletonDelayLock.class) { instance = new LazySingletonDelayLock (); } } return instance; } }
1.4. 懒汉式单例 - 双重检查锁定(无 volatile)
为了解决 1.3. 懒汉式单例 - 延迟锁 中提到的问题,我们进一步在同步块中添加检查,确认 instance
是否为空。如果为空,则进行实例化。这可以防止多线程场景下导致的多次实例化问题。
然而,由于 instance
字段没有标记为 volatile
,仍然可能由于指令重排序 导致错误。
在创建新对象(如 instance = new LazySingletonNoVolatileDoubleCheck()
)时,内部实际上涉及三个主要步骤:1. 内存分配
→ 2. 初始化
→ 3. 引用赋值
。
为了提高性能,JVM 允许对指令进行重排序,这三个步骤可能会被重新排列,如 1. 内存分配
→ 3. 引用赋值
→ 2. 初始化
,这可能会导致线程安全问题:
T1:进入临界区并准备 new
T1:内存分配
T1:引用赋值(因为此时尚未完成初始化,尽管 instance
现在指向新分配的堆空间,但对象只是部分初始化。)
T2:在第一次 if
检查时,发现 instance
不为空(T1 尚未完成完整初始化),直接返回了部分初始化的对象,导致程序错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class LazySingletonNoVolatileDoubleCheck { private static LazySingletonNoVolatileDoubleCheck instance; private LazySingletonNoVolatileDoubleCheck () { } public static LazySingletonNoVolatileDoubleCheck getInstance () { if (null == instance) { synchronized (LazySingletonNoVolatileDoubleCheck.class) { if (null == instance) { instance = new LazySingletonNoVolatileDoubleCheck (); } } } return instance; } }
我们已经发现,创建一个新对象的过程中包含了多个步骤。接下来,让我们从 Java 字节码的角度深入理解对象的创建过程。首先,我们编写如下简单的测试代码来创建一个对象:
1 2 3 4 5 public class NewTest { public static void main (String[] args) { NewTest newTest = new NewTest (); } }
我们可以通过 javac -g NewTest.java
编译 NewTest.java
以获取 NewTest.class
。然后,我们可以执行 javap -v -p NewTest.class
来获取类文件中字段、构造函数和方法的字节码信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 Classfile /DesignPattern-Lab/src/main/java/com/sissilab/dp/ox1_creational/ox11_singleton/NewTest.class Last modified 25 /07 /2024 ; size 487 bytes MD5 checksum e132b23dc7ec6394be9137ad5aa7c33a Compiled from "NewTest.java" public class com .sissilab.dp.ox1_creational.ox11_singleton.NewTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4. #19 #2 = Class #20 #3 = Methodref #2. #19 #4 = Class #21 #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 LocalVariableTable #10 = Utf8 this #11 = Utf8 Lcom/sissilab/dp/ox1_creational/ox11_singleton/NewTest; #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 args #15 = Utf8 [Ljava/lang/String; #16 = Utf8 newTest #17 = Utf8 SourceFile #18 = Utf8 NewTest.java #19 = NameAndType #5 :#6 #20 = Utf8 com/sissilab/dp/ox1_creational/ox11_singleton/NewTest #21 = Utf8 java/lang/Object { public com.sissilab.dp.ox1_creational.ox11_singleton.NewTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1 , locals=1 , args_size=1 0 : aload_0 1 : invokespecial #1 4 : return LineNumberTable: line 6 : 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/sissilab/dp/ox1_creational/ox11_singleton/NewTest; public static void main (java.lang.String[]) ; descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2 , locals=2 , args_size=1 0 : new #2 3 : dup 4 : invokespecial #3 7 : astore_1 8 : return LineNumberTable: line 8 : 0 line 9 : 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; 8 1 1 newTest Lcom/sissilab/dp/ox1_creational/ox11_singleton/NewTest; } SourceFile: "NewTest.java"
虽然只有几行简单的源代码,但反编译后的内容却很庞大。主要集中在下面的代码部分:
1 2 3 4 0 : new #2 3 : dup4 : invokespecial #3 7 : astore_1
下面是该过程的说明:
Heap Stack 0: new #2 The `new` instruction opens a space in the heap and a (ref) reference returned will be pushed onto the top of the stack. ref Heap Stack ref 3: dup The `dup` instruction duplicates the top-of-stack reference and pushes onto the top of the stack ref Heap Stack ref 4: invokespecial #3 The `invokespecial` instruction pops the top-of-stack reference and the just-allocated space will be initialized through invoking constructor Heap Stack ref 7: astore_1 The top-of-stack reference is popped and assigned to the element (`newTest`) at the position 1 of the LocalVariableTable LocalVariableTable 0 args 1 newTest
dup
指令需要复制栈顶元素的原因是因为下一步的 invokespecial
需要执行默认构造函数,这将从栈顶弹出一个引用。如果没有 dup
来复制栈顶元素,在执行 invokespecial
后将导致栈为空,从而导致新创建的对象丢失。
这解释了为什么 new
不是一个原子操作。尽管在 Java 代码中只有一行,但在字节码层面,它转化为四个关键指令操作。由于指令重排,new
中的三个步骤可以被重新排序。在多线程环境中,这可能导致上述单例模式的问题。因此,有必要在 instance
字段上添加 volatile
以防止重排。
1.5. 懒汉单例 - 双重检查锁定(DCL)
双重检查锁定是最推荐的懒汉初始化单例模式,确保了线程安全 ,并实现了延迟加载 。完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class LazySingletonDoubleCheck { private static volatile LazySingletonDoubleCheck instance; private LazySingletonDoubleCheck () { } public static LazySingletonDoubleCheck getInstance () { if (null == instance) { synchronized (LazySingletonDoubleCheck.class) { if (null == instance) { instance = new LazySingletonDoubleCheck (); } } } return instance; } }
1.5.1. 对“懒汉单例 - 双重检查锁定”的反射攻击
通常,为了避免反射攻击,我们通常在私有构造函数中添加检查,以查看 instance
字段是否为 null。如果 instance
不为 null,我们可以假设这个单例类已经创建了它的对象,并抛出异常以防止反射攻击。
然而,它容易受到反射攻击,这可能破坏预期的单例行为。我们来看以下两种情况:
情况 1 (√):如果我们先调用 getInstance()
然后调用 constructor.newInstance()
,将抛出错误以防止反射攻击。
情况 2 (×):如果我们先调用 constructor.newInstance()
然后调用 getInstance()
,我们将获得两个不同的实例,导致单例模式破裂。
因此,懒汉单例无法在所有情况下防止反射攻击。具体原因如下:
反射允许访问私有构造函数。攻击者可以使用反射机制绕过私有构造函数,创建单例类的新实例。
它缺乏任何固有机制来检查通过反射访问时单例类的实例是否已经存在。
在私有构造函数中抛出异常以防止反射攻击并不是一个万无一失的计划,因为攻击者仍然可以操控实例创建过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class LazySingletonDoubleCheckReflectionProof { private static volatile LazySingletonDoubleCheckReflectionProof instance; private LazySingletonDoubleCheckReflectionProof () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static LazySingletonDoubleCheckReflectionProof getInstance () { if (null == instance) { synchronized (LazySingletonDoubleCheckReflectionProof.class) { if (null == instance) { instance = new LazySingletonDoubleCheckReflectionProof (); } } } return instance; } }
1.5.2. 对“懒汉单例 - 双重检查锁定”的序列化攻击
序列化和反序列化一个类需要实现 java.io.Serializable
,否则将抛出 java.io.NotSerializableException
。序列化和反序列化的一般过程:
序列化单例类以生成序列化文件。
读取序列化文件通过反序列化获取单例类的实例。
比较序列化实例和反序列化实例是否相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class LazySingletonDoubleCheckSerialization implements Serializable { private static volatile LazySingletonDoubleCheckSerialization instance; private LazySingletonDoubleCheckSerialization () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static LazySingletonDoubleCheckSerialization getInstance () { if (null == instance) { synchronized (LazySingletonDoubleCheckSerialization.class) { if (null == instance) { instance = new LazySingletonDoubleCheckSerialization (); } } } return instance; } }
首先,我们将单例类序列化以生成序列化文件 -> 读取序列化文件并通过反序列化获取单例类的实例 -> 反序列化的实例与 getInstance()
的实例不相同,从而破坏单例模式。以下是测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test public void testSerializationAttackOnLazySingletonDoubleCheckSerialization () { LazySingletonDoubleCheckSerialization instance = LazySingletonDoubleCheckSerialization.getInstance(); final Path serializedClassPath = Paths.get("TestSerialization-" + LazySingletonDoubleCheckSerialization.class.getSimpleName()); try (ObjectOutputStream objectOutputStream = new ObjectOutputStream (Files.newOutputStream(serializedClassPath))) { objectOutputStream.writeObject(instance); } catch (IOException e) { throw new RuntimeException (e); } LazySingletonDoubleCheckSerialization testSerializableInstance; try (ObjectInputStream objectInputStream = new ObjectInputStream (Files.newInputStream(serializedClassPath))) { testSerializableInstance = ((LazySingletonDoubleCheckSerialization) objectInputStream.readObject()); } catch (IOException | ClassNotFoundException e) { throw new RuntimeException (e); } Assertions.assertSame(instance, testSerializableInstance); }
上述反序列化生成新对象的原因是序列化有其自己的机制。它读取字节流数据而不调用构造函数 。对于非枚举单例,为防止序列化攻击,需要添加版本号,如 private static final long serialVersionUID = -1L;
,并实现 readResolve()
方法,返回类的实例。这样就可以得到相同的对象。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class LazySingletonDoubleCheckSerializationProof implements Serializable { private static final long serialVersionUID = -1L ; private static volatile LazySingletonDoubleCheckSerializationProof instance; private LazySingletonDoubleCheckSerializationProof () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static LazySingletonDoubleCheckSerializationProof getInstance () { if (null == instance) { synchronized (LazySingletonDoubleCheckSerializationProof.class) { if (null == instance) { instance = new LazySingletonDoubleCheckSerializationProof (); } } } return instance; } Object readResolve () throws ObjectStreamException { return getInstance(); } }
我们来分析反序列化的源代码:objectInputStream.readObject()
-> readObject(Object.class)
-> Object obj = readObject0(type, false);
-> 在 TC_OBJECT 的情况下:readOrdinaryObject(unshared)
。
readOrdinaryObject(unshared)
的关键点是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 private Object readOrdinaryObject (boolean unshared) throws IOException { if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { if (rep != null ) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1 ); } } handles.setObject(passHandle, obj = rep); } } return obj; } boolean hasReadResolveMethod () { requireInitialized(); return (readResolveMethod != null ); } private ObjectStreamClass (final Class<?> cl) { readResolveMethod = getInheritableMethod(cl, "readResolve" , null , Object.class); }
1.5.3. 完美版的“懒汉单例 - 双重检查锁定”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public final class LazySingletonDoubleCheckPerfect implements Serializable { private static final long serialVersionUID = -1L ; private static volatile LazySingletonDoubleCheckPerfect instance; private LazySingletonDoubleCheckPerfect () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static LazySingletonDoubleCheckPerfect getInstance () { if (null == instance) { synchronized (LazySingletonDoubleCheckPerfect.class) { if (null == instance) { instance = new LazySingletonDoubleCheckPerfect (); } } } return instance; } private Object readResolve () throws ObjectStreamException { return getInstance(); } @Override protected Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException ("Cloning of this singleton instance is not allowed" ); } }
2. 饿汉式单例
饿汉式初始化模式在类加载时完成初始化,依赖于类加载机制 来确保单例行为而无需特地人为加锁,从而实现更高的执行效率。然而,由于在类加载时进行实例化,它缺乏延迟加载的优势。如果实例未被使用,它将被不必要地实例化,导致资源浪费。
在以下两个饿汉式单例的例子中,它们都比懒汉式单例简单得多。这两种模式都利用了 JVM 的类加载机制来确保实例的唯一性。初始化只发生一次,并且 JVM 会同步类加载过程。
类加载过程包括:
加载 :JVM 定位并将类文件的字节码加载到内存中,生成相应的类数据结构。
链接 :验证 (验证加载的字节码的正确性)→ 准备 (将静态字段初始化为默认值)→ 解析 (将类文件中的符号引用解析为实际引用)
初始化 :类中的静态初始化器 和静态代码块 在<clinit>
中执行,此过程仅在类首次被使用时发生,例如创建实例或调用静态方法时。
使用 :类被加载和初始化后,可以创建其实例,调用其方法以及访问其字段。
卸载 :当类及其相关的类加载器不再使用且可以被垃圾回收时,类会从内存中移除。
由于 instance
变量是静态成员变量,其实例化发生在类加载的初始化阶段。此阶段涉及执行类构造器 <clinit>()
方法,编译器会自动收集类中的所有静态变量和静态代码块。因此,private static final EagerSingletonStaticConstant instance = new EagerSingletonStaticConstant();
和 static { instance = new EagerSingletonStaticBlock(); }
也在此方法中执行。JVM 确保类的 <clinit>()
方法在多线程环境中是同步的,从而保证了线程安全。
Tip
有些人可能会困惑为什么饿汉式单例更浪费资源。他们可能会想:“当那个饿汉式单例类从未使用时,似乎没有调用类的构造函数。”
实际上,这种情况更多地出现在调用类的其他方法时,而不是调用getInstance()
方法。对于饿汉式单例,即使你不获取该单例的实例,仅仅是调用类的其他方法,也会由于类加载机制触发类的创建。但对于懒汉式单例,它不会触发类的创建,而只是执行该其他方法。
有关更多详细信息,请参阅此示例:EagerSingletonWastingReason.java at sissilab/DesignPattern-Lab (github.com) 。
2.1. 饿汉式单例 - 静态常量
这种实现非常简单,但可能会导致资源浪费,因为类的实例总是被创建,无论是否需要。而且在类创建期间处理异常也不容易。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class EagerSingletonStaticConstant { private static final EagerSingletonStaticConstant instance = new EagerSingletonStaticConstant (); private EagerSingletonStaticConstant () { } public static EagerSingletonStaticConstant getInstance () { return instance; } }
这段代码展示了饿汉式单例模式的简单实现,它依赖于静态常量来保证实例的唯一性和线程安全性。
2.1.1. 对“饿汉式单例 - 静态常量”的反射攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class EagerSingletonStaticConstantReflectionProof { private static final EagerSingletonStaticConstantReflectionProof instance = new EagerSingletonStaticConstantReflectionProof (); private EagerSingletonStaticConstantReflectionProof () { if (instance != null ) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static EagerSingletonStaticConstantReflectionProof getInstance () { return instance; } }
2.1.2. 对“饿汉式单例 - 静态常量”的序列化攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class EagerSingletonStaticConstantSerializationProof implements Serializable { private static final long serialVersionUID = -1L ; private static final EagerSingletonStaticConstantSerializationProof instance = new EagerSingletonStaticConstantSerializationProof (); private EagerSingletonStaticConstantSerializationProof () { if (instance != null ) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static EagerSingletonStaticConstantSerializationProof getInstance () { return instance; } Object readResolve () throws ObjectStreamException { return getInstance(); } }
2.1.3. 完美版的“饿汉式单例 - 静态常量”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public final class EagerSingletonStaticConstantPerfect implements Serializable { private static final long serialVersionUID = -1L ; private static final EagerSingletonStaticConstantPerfect instance = new EagerSingletonStaticConstantPerfect (); private EagerSingletonStaticConstantPerfect () { if (instance != null ) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static EagerSingletonStaticConstantPerfect getInstance () { return instance; } private Object readResolve () throws ObjectStreamException { return getInstance(); } @Override protected Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException ("Cloning of this singleton instance is not allowed" ); } }
2.2. 饿汉式单例 - 静态块
使用静态块完成类实例化也可能由于类加载机制导致资源浪费。但在静态块中处理异常和执行更多操作会更容易。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class EagerSingletonStaticBlock { private static final EagerSingletonStaticBlock instance; static { instance = new EagerSingletonStaticBlock (); } private EagerSingletonStaticBlock () { } public static EagerSingletonStaticBlock getInstance () { return instance; } }
2.2.1. 对“饿汉式单例 - 静态块”的反射攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class EagerSingletonStaticBlockReflectionProof { private static final EagerSingletonStaticBlockReflectionProof instance; static { instance = new EagerSingletonStaticBlockReflectionProof (); } private EagerSingletonStaticBlockReflectionProof () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static EagerSingletonStaticBlockReflectionProof getInstance () { return instance; } }
2.2.2. 对“饿汉式单例 - 静态块”的序列化攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class EagerSingletonStaticBlockSerializationProof implements Serializable { private static final long serialVersionUID = -1L ; private static final EagerSingletonStaticBlockSerializationProof instance; static { instance = new EagerSingletonStaticBlockSerializationProof (); } private EagerSingletonStaticBlockSerializationProof () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static EagerSingletonStaticBlockSerializationProof getInstance () { return instance; } Object readResolve () throws ObjectStreamException { return getInstance(); } }
2.2.3. 完美版本的“饿汉式单例 - 静态块”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public final class EagerSingletonStaticBlockPerfect implements Serializable { private static final long serialVersionUID = -1L ; private static final EagerSingletonStaticBlockPerfect instance; static { instance = new EagerSingletonStaticBlockPerfect (); } private EagerSingletonStaticBlockPerfect () { if (null != instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static EagerSingletonStaticBlockPerfect getInstance () { return instance; } private Object readResolve () throws ObjectStreamException { return getInstance(); } @Override protected Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException ("Cloning of this singleton instance is not allowed" ); } }
3. 静态内部类单例
静态内部类单例模式本质上也利用了类加载机制来确保线程安全。由于其特性,它可以保证类初始化仅在实际使用时触发,这也是一种懒加载的形式。
与 1.5. 懒汉单例 - 双重检查锁定(DCL) 相比,这种模式也能实现类似的懒加载效果,但实现更简单。
与 2. 饿汉式单例 相比,这种模式同样利用类加载机制来初始化并确保只有一个类实例。当类被加载时,饿汉式单例将被实例化,但静态内部类单例可能不会。这是因为实例只有在其静态内部类 InstanceHolder
被使用和加载时才会创建。因此,StaticInnerClassSingleton
的实例不会在方法 getInstance()
被调用之前创建,这样就实现了懒加载的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class StaticInnerClassSingleton { private static class InstanceHolder { private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton (); } private StaticInnerClassSingleton () { } public static StaticInnerClassSingleton getInstance () { return InstanceHolder.instance; } }
3.1. 对“静态内部类单例”的反射攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class StaticInnerSingletonReflectionProof { private static class InstanceHolder { private static StaticInnerSingletonReflectionProof instance = new StaticInnerSingletonReflectionProof (); } private StaticInnerSingletonReflectionProof () { if (null != InstanceHolder.instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static StaticInnerSingletonReflectionProof getInstance () { return InstanceHolder.instance; } }
3.2. 对“静态内部类单例”的序列化攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class StaticInnerSingletonSerializationProof implements Serializable { private static final long serialVersionUID = -1L ; private static class InstanceHolder { private static StaticInnerSingletonSerializationProof instance = new StaticInnerSingletonSerializationProof (); } private StaticInnerSingletonSerializationProof () { if (null != InstanceHolder.instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static StaticInnerSingletonSerializationProof getInstance () { return InstanceHolder.instance; } Object readResolve () throws ObjectStreamException { return getInstance(); } }
3.3. 完美版本的“静态内部类单例”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public final class StaticInnerSingletonPerfect implements Serializable { private static final long serialVersionUID = -1L ; private static class InstanceHolder { private static StaticInnerSingletonPerfect instance = new StaticInnerSingletonPerfect (); } private StaticInnerSingletonPerfect () { if (null != InstanceHolder.instance) { throw new IllegalStateException ("This singleton instance already exists." ); } } public static StaticInnerSingletonPerfect getInstance () { return StaticInnerSingletonPerfect.InstanceHolder.instance; } Object readResolve () throws ObjectStreamException { return getInstance(); } @Override protected Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException ("Cloning of this singleton instance is not allowed" ); } }
4. 枚举型单例
枚举型单例被认为是实现单例模式的最佳方式。它不仅利用了类加载机制来保证线程安全,还防止在反序列化过程中重建对象,并避免了反射攻击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public enum EnumSingleton { instance; public void anyMethod () { } }
让我们分析一下枚举类的字节码。在编译 EnumSingleton.java
并通过 javac -g EnumSingleton.java
生成类文件后,执行 javap -v -p EnumSingleton.class
以生成字节码文件,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 Classfile /DesignPattern-Lab/src/main/java/com/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton.class Last modified 26 /08/2024 ; size 1195 bytes MD5 checksum 1e21098493b1502d48f3a4fba5884dcc Compiled from "EnumSingleton.java" public final class com .sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton extends java .lang.Enum<com.sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton> minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM Constant pool: #1 = Fieldref #4. #34 #2 = Methodref #35. #36 #3 = Class #14 #4 = Class #37 #5 = Methodref #10. #38 #6 = Methodref #10. #39 #7 = String #11 #8 = Methodref #4. #39 #9 = Fieldref #4. #40 #10 = Class #41 #11 = Utf8 instance #12 = Utf8 Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; #13 = Utf8 $VALUES #14 = Utf8 [Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; #15 = Utf8 values #16 = Utf8 ()[Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 valueOf #20 = Utf8 (Ljava/lang/String;)Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; #21 = Utf8 LocalVariableTable #22 = Utf8 name #23 = Utf8 Ljava/lang/String; #24 = Utf8 <init> #25 = Utf8 (Ljava/lang/String;I)V #26 = Utf8 this #27 = Utf8 Signature #28 = Utf8 ()V #29 = Utf8 anyMethod #30 = Utf8 <clinit> #31 = Utf8 Ljava/lang/Enum<Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton;>; #32 = Utf8 SourceFile #33 = Utf8 EnumSingleton.java #34 = NameAndType #13 :#14 #35 = Class #14 #36 = NameAndType #42 :#43 #37 = Utf8 com/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton #38 = NameAndType #19 :#44 #39 = NameAndType #24 :#25 #40 = NameAndType #11 :#12 #41 = Utf8 java/lang/Enum #42 = Utf8 clone #43 = Utf8 ()Ljava/lang/Object; #44 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; { public static final com.sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton instance; descriptor: Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM private static final com.sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton[] $VALUES; descriptor: [Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC public static com.sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton[] values(); descriptor: ()[Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; flags: ACC_PUBLIC, ACC_STATIC Code: stack=1 , locals=0 , args_size=0 0 : getstatic #1 3 : invokevirtual #2 6 : checkcast #3 9 : areturn LineNumberTable: line 17 : 0 public static com.sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton valueOf (java.lang.String) ; descriptor: (Ljava/lang/String;)Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; flags: ACC_PUBLIC, ACC_STATIC Code: stack=2 , locals=1 , args_size=1 0 : ldc #4 2 : aload_0 3 : invokestatic #5 6 : checkcast #4 9 : areturn LineNumberTable: line 17 : 0 LocalVariableTable: Start Length Slot Name Signature 0 10 0 name Ljava/lang/String; private com.sissilab.dp.ox1_creational.ox11_singleton.EnumSingleton(); descriptor: (Ljava/lang/String;I)V flags: ACC_PRIVATE Code: stack=3 , locals=3 , args_size=3 0 : aload_0 1 : aload_1 2 : iload_2 3 : invokespecial #6 6 : return LineNumberTable: line 17 : 0 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; Signature: #28 public void anyMethod () ; descriptor: ()V flags: ACC_PUBLIC Code: stack=0 , locals=1 , args_size=1 0 : return LineNumberTable: line 21 : 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Lcom/sissilab/dp/ox1_creational/ox11_singleton/EnumSingleton; static {}; descriptor: ()V flags: ACC_STATIC Code: stack=4 , locals=0 , args_size=0 0 : new #4 3 : dup 4 : ldc #7 6 : iconst_0 7 : invokespecial #8 10 : putstatic #9 13 : iconst_1 14 : anewarray #4 17 : dup 18 : iconst_0 19 : getstatic #9 22 : aastore 23 : putstatic #1 26 : return LineNumberTable: line 18 : 0 line 17 : 13 } Signature: #31 SourceFile: "EnumSingleton.java"
实际上,初始化过程发生在静态初始化块(static {}
,它会在类首次加载时执行),关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0 : new #4 3 : dup4 : ldc #7 6 : iconst_07 : invokespecial #8 10 : putstatic #9
4.1. 对“枚举型单例”的反射攻击
当尝试通过反射获取枚举类的实例时,会抛出错误:java.lang.IllegalArgumentException: Cannot reflectively create enum objects
。这有效地防止了反射攻击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void testReflectionAttackOnEnumSingleton () throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int .class); constructor.setAccessible(true ); EnumSingleton reflectInstance = constructor.newInstance("instance" , 0 ); EnumSingleton instance = EnumSingleton.instance; Assertions.assertSame(reflectInstance, instance); }
实际上,错误是在 public T newInstance(Object ... initargs)
中抛出的。以下源代码表明有一个显式检查当前类是否为枚举类型,如果是,则会抛出 IllegalArgumentException
。因此,枚举单例可以有效防止反射攻击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public final class Constructor <T> extends Executable { @CallerSensitive public T newInstance (Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null , modifiers); } } if ((clazz.getModifiers() & Modifier.ENUM) != 0 ) throw new IllegalArgumentException ("Cannot reflectively create enum objects" ); ConstructorAccessor ca = constructorAccessor; if (ca == null ) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; } }
4.2. 对“枚举型单例”的序列化攻击
每个枚举类隐式继承自 java.lang.Enum
抽象类,该类实现了 java.io.Serializable
,这意味着枚举类可以支持序列化和反序列化。得益于其内置机制,枚举单例天然能够抵御序列化攻击,因为在反序列化过程中不会创建新的枚举常量实例。
让我们看看在反序列化源代码中枚举单例发生了什么。在 readObject0(type, false)
方法中,针对 TC_ENUM
的情况:–> readEnum(boolean unshared)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 private Enum<?> readEnum(boolean unshared) throws IOException { if (bin.readByte() != TC_ENUM) { throw new InternalError (); } ObjectStreamClass desc = readClassDesc(false ); if (!desc.isEnum()) { throw new InvalidClassException ("non-enum class: " + desc); } int enumHandle = handles.assign(unshared ? unsharedMarker : null ); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null ) { handles.markException(enumHandle, resolveEx); } String name = readString(false ); Enum<?> result = null ; Class<?> cl = desc.forClass(); if (cl != null ) { try { @SuppressWarnings("unchecked") Enum<?> en = Enum.valueOf((Class)cl, name); result = en; } catch (IllegalArgumentException ex) { throw (IOException) new InvalidObjectException ( "enum constant " + name + " does not exist in " + cl).initCause(ex); } if (!unshared) { handles.setObject(enumHandle, result); } } handles.finish(enumHandle); passHandle = enumHandle; return result; } public static <T extends Enum <T>> T valueOf (Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null ) return result; if (name == null ) throw new NullPointerException ("Name is null" ); throw new IllegalArgumentException ( "No enum constant " + enumType.getCanonicalName() + "." + name); }
4.3. 完美版本的“枚举型单例”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public enum EnumSingletonPerfect { instance; public void anyMethod () { } private static class LazyHeavyResource { private static final HeavyResource RESOURCE = new HeavyResource (); } public HeavyResource getExpensiveResource () { return LazyHeavyResource.RESOURCE; } private static class HeavyResource { HeavyResource() { try { TimeUnit.SECONDS.sleep(3 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } void useResource () { System.out.println("Use this heavy resource..." ); } } }
5. 在源代码中的应用
5.1. java.lang.Runtime (饿汉式单例 - 静态常量)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Runtime { private static Runtime currentRuntime = new Runtime (); private Runtime () {} public static Runtime getRuntime () { return currentRuntime; } }
5.2. org.quartz.impl.SchedulerRepository (懒汉式单例 - synchronized)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class SchedulerRepository { private static SchedulerRepository inst; private SchedulerRepository () {} public static synchronized SchedulerRepository getInstance () { if (inst == null ) { inst = new SchedulerRepository (); } return inst; } }
5.3. Spring - org.springframework.beans.factory.support.DefaultSingletonBeanRegistry (懒汉式单例 - 双重检查锁定 DCL)
Spring 的单例模式:当在 Spring 应用程序上下文中定义一个 bean 时,如果没有明确指定作用域,Spring 会将其视为单例。这意味着 Spring 只会创建一个实例,该实例可以被所有类共享。通过在 Spring 中利用这种单例模式,我们可以享受到高效的内存管理、集中化的 bean 配置,以及在整个应用程序中共享和重用 bean 实例的好处。
在 Spring 中,加载单例的过程通常从 org.springframework.beans.factory.BeanFactory
的 getBean()
方法开始,其默认实现是抽象类 org.springframework.beans.factory.support.AbstractBeanFactory
。各种 getBean()
方法最终调用 AbstractBeanFactory
的 doGetBean()
方法,例如 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { protected <T> T doGetBean (final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null ) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null ); } else { try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory <Object>() { @Override public Object getObject () throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { } else { } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } return (T) bean; } }
该代码 Object sharedInstance = getSingleton(beanName);
在 getBean()
方法中是 Spring 中单例模式(双重检查)的核心实现。getSingleton()
方法在 AbstractBeanFactory
的父类 DefaultSingletonBeanRegistry
中定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { private final Map<String, Object> singletonObjects = new ConcurrentHashMap <>(256 ); private final Map<String, Object> earlySingletonObjects = new HashMap <>(16 ); private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap <>(16 ); @Override public Object getSingleton (String beanName) { return getSingleton(beanName, true ); } protected Object getSingleton (String beanName, boolean allowEarlyReference) { Object singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this .singletonObjects) { singletonObject = this .earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this .singletonFactories.get(beanName); if (singletonFactory != null ) { singletonObject = singletonFactory.getObject(); this .earlySingletonObjects.put(beanName, singletonObject); this .singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null ); } }
一般来说,当第一次在 doGetBean()
中调用 getSingleton()
时,缓存中找不到对应的 bean 实例。然后,它进入 if (mbd.isSingleton()) { }
块,调用重载的 getSingleton()
方法。在此方法中,bean 被实例化,然后实例被缓存到 singletonObjects
映射中,从而允许在后续请求中直接返回单例 bean。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { public Object getSingleton (String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null" ); synchronized (this .singletonObjects) { Object singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null ) { if (this .singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException (beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)" ); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'" ); } beforeSingletonCreation(beanName); boolean newSingleton = false ; boolean recordSuppressedExceptions = (this .suppressedExceptions == null ); if (recordSuppressedExceptions) { this .suppressedExceptions = new LinkedHashSet <Exception>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true ; } catch (IllegalStateException ex) { singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null ) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this .suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this .suppressedExceptions = null ; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null ); } } }
5.4. Spring - org.springframework.core.ReactiveAdapterRegistry (懒汉式单例 - 双重检查锁定 DCL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class ReactiveAdapterRegistry { @Nullable private static volatile ReactiveAdapterRegistry sharedInstance; public static ReactiveAdapterRegistry getSharedInstance () { ReactiveAdapterRegistry registry = sharedInstance; if (registry == null ) { synchronized (ReactiveAdapterRegistry.class) { registry = sharedInstance; if (registry == null ) { registry = new ReactiveAdapterRegistry (); sharedInstance = registry; } } } return registry; } }
5.5. Spring - org.springframework.aop.framework.ProxyFactoryBean (懒汉式单例 - synchronized)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean <Object>, BeanClassLoaderAware, BeanFactoryAware { private Object singletonInstance; private synchronized Object getSingletonInstance () { if (this .singletonInstance == null ) { this .targetSource = freshTargetSource(); if (this .autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) { Class<?> targetClass = getTargetClass(); if (targetClass == null ) { throw new FactoryBeanNotInitializedException ("Cannot determine target class for proxy" ); } setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this .proxyClassLoader)); } super .setFrozen(this .freezeProxy); this .singletonInstance = getProxy(createAopProxy()); } return this .singletonInstance; } }
5.6. Tomcat - org.apache.catalina.webresources.TomcatURLStreamHandlerFactory (懒汉式单例 - 双重检查锁定 DCL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class TomcatURLStreamHandlerFactory implements URLStreamHandlerFactory { private static volatile TomcatURLStreamHandlerFactory instance = null ; public static TomcatURLStreamHandlerFactory getInstance () { getInstanceInternal(true ); return instance; } private static TomcatURLStreamHandlerFactory getInstanceInternal (boolean register) { if (instance == null ) { Class var1 = TomcatURLStreamHandlerFactory.class; synchronized (TomcatURLStreamHandlerFactory.class) { if (instance == null ) { instance = new TomcatURLStreamHandlerFactory (register); } } } return instance; } }
References