@@ -40,11 +40,40 @@ public class Singleton {
4040
4141饿汉式单例的** 缺点** :单例对象的创建,不是** 延时加载** 。
4242
43- ## 双重检查锁定
43+ ## 懒汉式
4444
45- 双重校验锁先判断 instance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁 。
45+ 与饿汉式思想不同,懒汉式支持延时加载,将对象的创建延迟到了获取对象的时候。不过为了线程安全,在获取对象的操作需要加锁,这就导致了低性能 。
4646
47- instance使用static修饰的原因:getInstance为静态方法,因为静态方法的内部不能直接使用非静态变量,只有静态成员才能在没有创建对象时进行初始化,所以返回的这个实例必须是静态的。
47+ ``` java
48+ public class Singleton {
49+ private static final Singleton instance;
50+
51+ private Singleton () {}
52+
53+ public static synchronized Singleton getInstance () {
54+ if (instance == null ) {
55+ instance = new Singleton ();
56+ }
57+
58+ return instance;
59+ }
60+ }
61+ ```
62+
63+ 上述代码加的锁只有在第一次创建对象时有用,而之后每次获取对象,其实是不需要加锁的(双重检查锁定优化了这个问题)。
64+
65+ 懒汉式单例** 优点** :
66+
67+ - 对象的创建是线程安全的。
68+ - 支持延时加载。
69+
70+ 懒汉式单例** 缺点** :
71+
72+ - 获取对象的操作被加上了锁,影响了并发性能。
73+
74+ ## 双重检查锁定
75+
76+ 双重检查锁定将懒汉式中的 ` synchronized ` 方法改成了 ` synchronized ` 代码块。如下:
4877
4978```
5079public class Singleton {
@@ -62,6 +91,10 @@ public class Singleton {
6291 }
6392}
6493```
94+ 双重校验锁先判断 instance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
95+
96+ instance使用static修饰的原因:getInstance为静态方法,因为静态方法的内部不能直接使用非静态变量,只有静态成员才能在没有创建对象时进行初始化,所以返回的这个实例必须是静态的。
97+
6598为什么两次判断` instance == null ` :
6699
67100| Time | Thread A | Thread B |
@@ -94,12 +127,16 @@ instance = memory; // 3:设置instance指向刚分配的内存地址
94127| T7 | | 访问` instance ` (此时对象还未完成初始化) |
95128| T8 | 初始化` instance ` | |
96129
130+ 双重检查锁定单例** 优点** :
131+
132+ - 对象的创建是线程安全的。
133+ - 支持延时加载。
134+ - 获取对象时不需要加锁。
135+
97136## 静态内部类
98137
99138它与饿汉模式一样,也是利用了类初始化机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
100139
101- ![ ] ( http://img.dabin-coder.cn/image/singleton-class-init.png )
102-
103140基于类初始化的方案的实现代码更简洁。
104141
105142```
@@ -113,5 +150,23 @@ public class Instance {
113150 }
114151}
115152```
116- 但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。
153+ 如上述代码,` InstanceHolder ` 是一个静态内部类,当外部类 ` Instance ` 被加载的时候,并不会创建 ` InstanceHolder ` 实例对象。
154+
155+ 只有当调用 ` getInstance() ` 方法时,` InstanceHolder ` 才会被加载,这个时候才会创建 ` Instance ` 。` Instance ` 的唯一性、创建过程的线程安全性,都由 JVM 来保证。
156+
157+ 静态内部类单例** 优点** :
158+
159+ - 对象的创建是线程安全的。
160+ - 支持延时加载。
161+ - 获取对象时不需要加锁。
162+
163+ ## 枚举
164+
165+ 用枚举来实现单例,是最简单的方式。这种实现方式通过 ** Java 枚举** 类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。
166+
167+ ``` java
168+ public enum Singleton {
169+ INSTANCE ; // 该对象全局唯一
170+ }
171+ ```
117172
0 commit comments