1 单例模式(Singleton)
1.1 定义
1.2 场景
- 重量级的对象,不需要多个实例,如线程池、数据库连接池等。
1.3 类图
1.4 实现
1、懒汉式
- 延迟加载,只有在真正使用的时候,才开始实例化。
- 问题:
- 线程安全问题。
double check
加锁优化。 - 编译器(JIT)、CPU有可能对指令进行重排序,导致使用到尚未初始化的实例。可以通过添加
volatile
关键字进行修饰。对于volatile
修饰的字段,可以防止指令重排。
1)一般方式
package com.xianhuii.designpattern.singleton;
public class LazySingleton {
private volatile static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
- 反汇编
javap -v LazySingleton.class
:
Classfile /D:/exam/out/production/exam/com/xianhuii/designpattern/singleton/LazySingleton.class
Last modified 2020年5月14日; size 624 bytes
MD5 checksum 8fe7d4bc5ddd17a3ab812ce299308287
Compiled from "LazySingleton.java"
public class com.xianhuii.designpattern.singleton.LazySingleton
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #3 // com/xianhuii/designpattern/singleton/LazySingleton
super_class: #5 // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #5.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#21 // com/xianhuii/designpattern/singleton/LazySingleton.instance:Lcom/xianhuii/designpattern/singleton
/LazySingleton;
#3 = Class #22 // com/xianhuii/designpattern/singleton/LazySingleton
#4 = Methodref #3.#20 // com/xianhuii/designpattern/singleton/LazySingleton."<init>":()V
#5 = Class #23 // java/lang/Object
#6 = Utf8 instance
#7 = Utf8 Lcom/xianhuii/designpattern/singleton/LazySingleton;
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 getInstance
#15 = Utf8 ()Lcom/xianhuii/designpattern/singleton/LazySingleton;
#16 = Utf8 StackMapTable
#17 = Class #24 // java/lang/Throwable
#18 = Utf8 SourceFile
#19 = Utf8 LazySingleton.java
#20 = NameAndType #8:#9 // "<init>":()V
#21 = NameAndType #6:#7 // instance:Lcom/xianhuii/designpattern/singleton/LazySingleton;
#22 = Utf8 com/xianhuii/designpattern/singleton/LazySingleton
#23 = Utf8 java/lang/Object
#24 = Utf8 java/lang/Throwable
{
public static com.xianhuii.designpattern.singleton.LazySingleton getInstance();
descriptor: ()Lcom/xianhuii/designpattern/singleton/LazySingleton;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=0
0: getstatic #2 // Field instance:Lcom/xianhuii/designpattern/singleton/LazySingleton;
3: ifnonnull 37
6: ldc #3 // class com/xianhuii/designpattern/singleton/LazySingleton
8: dup
9: astore_0
10: monitorenter
11: getstatic #2 // Field instance:Lcom/xianhuii/designpattern/singleton/LazySingleton;
14: ifnonnull 27
17: new #3 // class com/xianhuii/designpattern/singleton/LazySingleton
20: dup
21: invokespecial #4 // Method "<init>":()V
24: putstatic #2 // Field instance:Lcom/xianhuii/designpattern/singleton/LazySingleton;
27: aload_0
28: monitorexit
29: goto 37
32: astore_1
33: aload_0
34: monitorexit
35: aload_1
36: athrow
37: getstatic #2 // Field instance:Lcom/xianhuii/designpattern/singleton/LazySingleton;
40: areturn
Exception table:
from to target type
11 29 32 any
32 35 32 any
LineNumberTable:
line 10: 0
line 11: 6
line 12: 11
line 13: 17
line 15: 27
line 17: 37
StackMapTable: number_of_entries = 3
frame_type = 252 /* append */
offset_delta = 27
locals = [ class java/lang/Object ]
frame_type = 68 /* same_locals_1_stack_item */
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
SourceFile: "LazySingleton.java"
2)静态内部类方式
- 本质上借助于JVM的类加载机制,保证实例的唯一性。
- 只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种方式。
package com.xianhuii.designpattern.singleton;
public class InnerClassLazySingleton {
private InnerClassLazySingleton(){}
private static class Inner {
private static InnerClassLazySingleton instance = new InnerClassLazySingleton();
}
public static InnerClassLazySingleton getInstance() {
return Inner.instance;
}
}
package com.xianhuii;
import com.xianhuii.designpattern.singleton.InnerClassLazySingleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class MyTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<InnerClassLazySingleton> cons = InnerClassLazySingleton.class.getDeclaredConstructor();
cons.setAccessible(true);
InnerClassLazySingleton instance1 = cons.newInstance();
InnerClassLazySingleton instance2 = InnerClassLazySingleton.getInstance();
System.out.println(instance1 == instance2); // false
}
}
package com.xianhuii.designpattern.singleton;
import java.io.*;
public class InnerClassLazySingleton implements Serializable {
private static final long serialVersionUID = 42L;
private InnerClassLazySingleton(){}
private static class Inner {
private static InnerClassLazySingleton instance = new InnerClassLazySingleton();
}
public static InnerClassLazySingleton getInstance() {
return Inner.instance;
}
// private Object readResolve() throws ObjectStreamException {
// return Inner.instance;
// }
public static void main(String[] args) throws Exception {
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(new File("test")));
InnerClassLazySingleton instance1 = InnerClassLazySingleton.getInstance();
output.writeObject(instance1);
ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("test")));
InnerClassLazySingleton instance2 = (InnerClassLazySingleton) input.readObject();
System.out.println(instance1 == instance2); // false
}
}
2、饿汉式
- 类加载的初始化阶段就完成了实例的初始化。
- 本质上借助于JVM的类加载机制,保证实例的唯一性。
- 类加载过程:
- 加载:加载二进制数据到内存中,生成对应的Class数据结构。
- 连接:验证、准备(给类的静态成员变量赋默认值)、解析。
- 初始化:给类的静态变量赋初值。
- 只有在真正使用对应的类时,才会触发初始化:
- 当前类是启动类,即main函数所在类。
- 直接进行new操作。
- 访问类的静态属性、静态方法。
- 使用反射访问类。
- 初始化一个类的子类。
1)一般方式
package com.xianhuii.designpattern.singleton;
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
2)枚举方式
package com.xianhuii.designpattern.singleton;
public enum EnumSingleton {
INSTANCE;
}
- 反汇编
javap -v -p EnumSingleton.class
:
Classfile /D:/exam/out/production/exam/com/xianhuii/designpattern/singleton/EnumSingleton.class
Last modified 2020年5月14日; size 1072 bytes
MD5 checksum 7ae8019a05f48d1a3c1ca40eb711180d
Compiled from "EnumSingleton.java"
public final class com.xianhuii.designpattern.singleton.EnumSingleton extends java.lang.Enum<com.xianhuii.designpattern.singleton.EnumSingleto
n>
minor version: 0
major version: 55
flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
this_class: #4 // com/xianhuii/designpattern/singleton/EnumSingleton
super_class: #10 // java/lang/Enum
interfaces: 0, fields: 2, methods: 4, attributes: 2
Constant pool:
#1 = Fieldref #4.#33 // com/xianhuii/designpattern/singleton/EnumSingleton.$VALUES:[Lcom/xianhuii/designpattern/singleton
/EnumSingleton;
#2 = Methodref #34.#35 // "[Lcom/xianhuii/designpattern/singleton/EnumSingleton;".clone:()Ljava/lang/Object;
#3 = Class #14 // "[Lcom/xianhuii/designpattern/singleton/EnumSingleton;"
#4 = Class #36 // com/xianhuii/designpattern/singleton/EnumSingleton
#5 = Methodref #10.#37 // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#6 = Methodref #10.#38 // java/lang/Enum."<init>":(Ljava/lang/String;I)V
#7 = String #11 // INSTANCE
#8 = Methodref #4.#38 // com/xianhuii/designpattern/singleton/EnumSingleton."<init>":(Ljava/lang/String;I)V
#9 = Fieldref #4.#39 // com/xianhuii/designpattern/singleton/EnumSingleton.INSTANCE:Lcom/xianhuii/designpattern/singleton
/EnumSingleton;
#10 = Class #40 // java/lang/Enum
#11 = Utf8 INSTANCE
#12 = Utf8 Lcom/xianhuii/designpattern/singleton/EnumSingleton;
#13 = Utf8 $VALUES
#14 = Utf8 [Lcom/xianhuii/designpattern/singleton/EnumSingleton;
#15 = Utf8 values
#16 = Utf8 ()[Lcom/xianhuii/designpattern/singleton/EnumSingleton;
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 valueOf
#20 = Utf8 (Ljava/lang/String;)Lcom/xianhuii/designpattern/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 <clinit>
#30 = Utf8 Ljava/lang/Enum<Lcom/xianhuii/designpattern/singleton/EnumSingleton;>;
#31 = Utf8 SourceFile
#32 = Utf8 EnumSingleton.java
#33 = NameAndType #13:#14 // $VALUES:[Lcom/xianhuii/designpattern/singleton/EnumSingleton;
#34 = Class #14 // "[Lcom/xianhuii/designpattern/singleton/EnumSingleton;"
#35 = NameAndType #41:#42 // clone:()Ljava/lang/Object;
#36 = Utf8 com/xianhuii/designpattern/singleton/EnumSingleton
#37 = NameAndType #19:#43 // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#38 = NameAndType #24:#25 // "<init>":(Ljava/lang/String;I)V
#39 = NameAndType #11:#12 // INSTANCE:Lcom/xianhuii/designpattern/singleton/EnumSingleton;
#40 = Utf8 java/lang/Enum
#41 = Utf8 clone
#42 = Utf8 ()Ljava/lang/Object;
#43 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
public static final com.xianhuii.designpattern.singleton.EnumSingleton INSTANCE;
descriptor: Lcom/xianhuii/designpattern/singleton/EnumSingleton;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
private static final com.xianhuii.designpattern.singleton.EnumSingleton[] $VALUES;
descriptor: [Lcom/xianhuii/designpattern/singleton/EnumSingleton;
flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
public static com.xianhuii.designpattern.singleton.EnumSingleton[] values();
descriptor: ()[Lcom/xianhuii/designpattern/singleton/EnumSingleton;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field $VALUES:[Lcom/xianhuii/designpattern/singleton/EnumSingleton;
3: invokevirtual #2 // Method "[Lcom/xianhuii/designpattern/singleton/EnumSingleton;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lcom/xianhuii/designpattern/singleton/EnumSingleton;"
9: areturn
LineNumberTable:
line 3: 0
public static com.xianhuii.designpattern.singleton.EnumSingleton valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/xianhuii/designpattern/singleton/EnumSingleton;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #4 // class com/xianhuii/designpattern/singleton/EnumSingleton
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class com/xianhuii/designpattern/singleton/EnumSingleton
9: areturn
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 name Ljava/lang/String;
private com.xianhuii.designpattern.singleton.EnumSingleton();
descriptor: (Ljava/lang/String;I)V
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/xianhuii/designpattern/singleton/EnumSingleton;
Signature: #28 // ()V
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=4, locals=0, args_size=0
0: new #4 // class com/xianhuii/designpattern/singleton/EnumSingleton
3: dup
4: ldc #7 // String INSTANCE
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field INSTANCE:Lcom/xianhuii/designpattern/singleton/EnumSingleton;
13: iconst_1
14: anewarray #4 // class com/xianhuii/designpattern/singleton/EnumSingleton
17: dup
18: iconst_0
19: getstatic #9 // Field INSTANCE:Lcom/xianhuii/designpattern/singleton/EnumSingleton;
22: aastore
23: putstatic #1 // Field $VALUES:[Lcom/xianhuii/designpattern/singleton/EnumSingleton;
26: return
LineNumberTable:
line 4: 0
line 3: 13
}
Signature: #30 // Ljava/lang/Enum<Lcom/xianhuii/designpattern/singleton/EnumSingleton;>;
SourceFile: "EnumSingleton.java"