java双锁_单例模式的双重加锁实现
本⽂从单例模式的⼀般实现⽅式开始说起,逐步深⼊到双重加锁实现。
1. ⾸先介绍⼀下最简单的单例模式——饿汉模式,这种⽅式在单例类被加载的时候实例化。代码实现如下:
1 public classSingleton {
2 private staticSingleton instance;3
4 static{
单例模式的几种实现方式5 instance = newSingleton();
6 }7
8 privateSingleton() {9 }10
11 public staticSingleton getInstance() {12 returninstance;13 }14 }
饿汉模式的缺点在于,如果单例对象的创建过程⽐较耗时,那么应⽤程序的启动将会⽐较慢。
2. 为了克服饿汉模式的缺点,将单例对象的创建过程延后到第⼀次使⽤单例对象时,这种实现⽅式被称为懒汉模式。代码实现如下:
1 public classSingleton {
2 private staticSingleton instance;3
4 privateSingleton() {
5 }6
7 public staticSingleton getInstance() {8 if (instance == null) {9 instance = newSingleton();10 }11
12 returninstance;13 }14 }
需要注意的是这种实现⽅式是线程不安全的。假设在单例类被实例化之前,有两个线程同时在获取单例对象,线程1在执⾏完第8⾏ if (instance == null) 后,线程调度机制将 CPU 资源分配给线程2,此时线程2在执⾏第8⾏  if (instance == null) 时也发现单例类还没有被实例化,这样就会导致单例类被实例化两次。为了防⽌这种情况发⽣,需要对 getInstance() ⽅法同步。下⾯看改进后的懒汉模式:
1 public classSingleton {
2 private staticSingleton instance;3
4 privateSingleton() {
5 }6
7 //线程安全的懒汉模式
8 public synchronized staticSingleton getInstance() {9 if (instance == null) {10 instance = newSingleton();11 }12
13 returninstance;14 }15 }
3. 双重加锁(double check)
第2种实现⽅式中,每次获取单例对象时都会加锁,这样就会带来性能损失。双重加锁实现本质也是⼀种懒汉模式,相⽐第2种实现⽅式将会有较⼤的性能提升。代码实现如下:
1 public classSingleton {
2 private volatile staticSingleton instance;3
4 privateSingleton() {
5 }6
7 public staticSingleton getInstance() {8 if (instance == null) {9 synchronized (Singleton.class) {10 if (instance == null) {11 instance = newSingleton();12 }13 }14 }15
16 returninstance;17 }18 }
就算在单例类被实例化时有多个线程同时通过了第8⾏代码 if (instance == null) 的判断,但同⼀时间只有⼀个线程获得锁后进⼊临界区。通过第8⾏判断的每个线程会依次获得锁进⼊临界区,所以进⼊临界区后还要再判断⼀次单例类是否已被其它线程实例化,以避免多次实例化。由于双重加锁实现仅在实例化单例类时需要加锁,所以相较于第2种实现⽅式会带来性能上的提升。另外需要注意的是双重加锁要对instance 域加上 volatile 修饰符。由于 synchronized 并不是对 instance 实例进⾏加锁(因为现在还并没有实例),所以线程在执⾏完第11⾏修改 instance 的值后,应该将修改后的 instance ⽴即写⼊主存(main memory),⽽不是暂时存在寄存器或者⾼速缓冲区(caches)中,以保证新的值对其它线程可见。
补充:第9⾏可以锁住任何⼀个对象,要进⼊临界区必须获得这个对象的锁。由于并不知道其它对象的锁的⽤途,所以这⾥最好的⽅式是对Singleton.class 进⾏加锁。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。