饿汉式单例、懒汉式单例和双重检查锁单例模式

饿汉式单例模式:

public class Hungry {

	private Hungry() {}
	private final static Hungry HUNGRY = new Hungry();
	
	public static Hungry getInstance() {
		return HUNGRY;
	}
}

懒汉式单例:

public class lazyMan {

	private lazyMan() {}
	private static lazyMan LAZY;
	
	public static lazyMan getInstance() {
		if(LAZY == null) {           //1:A线程执行
			LAZY = new lazyMan();	 //2:B线程执行
		}
		return LAZY;
	}
}

懒汉式单例模式的问题

首先声明一点——代码2这一行可以分解成:

  1. 分配对象的内存空间
  2. 初始化对象
  3. 设置LAZY指向刚分配的内存地址

上边是我们任务的顺序,但是编译器可能对这一行代码优化成:

  1. 分配对象的内存空间
  2. 设置LAZY指向刚分配的内存地址
  3. 初始化对象

假设A线程执行代码1的同时,B线程执行代码2,此时线程A可能会看到LAZY引用的对象还没有完成初始化,这肯定会出错;或者另一种情况,线程A和B同时执行到代码1,最终同时进入代码块new对象,返回了两个不同的对象;

解决方案

我们可以对getInstance()方法进行同步处理来解决这种问题:

public class lazyMan {

	private lazyMan() {}
	private static lazyMan LAZY;
	
	public synchronized static lazyMan getInstance() {
		if(LAZY == null) {
			LAZY = new lazyMan();
		}
		return LAZY;
	}
}

那么问题来了,我们都知道synchronized 是一个重量级锁,如果getInstance()方法被多个线程频繁调用,将会导致程序执行性能的下降。

解决方案(双重检查锁)

public class lazyMan {

	private lazyMan() {}
	private volatile static lazyMan LAZY;     //1
	
	public static lazyMan getInstance() {
		if(LAZY == null) {					 //2
			synchronized (lazyMan.class) {
				if(LAZY == null)
					LAZY = new lazyMan();
			}
		}
		return LAZY;
	}
}

如上面的代码:

  1. 多个线程同一时间创建对象时,会通过加锁保证只有一个线程创建锁;
  2. 如果检查LAZY不为空那么就不需要执行下面的加锁和初始化操作,可以大幅的减少synchronized带来的性能开销。
  3. 代码1处使用volatile
    修饰也很有必要,因为synchronized并不能禁止指令重排,使用volatile才能保证多线程情况下线程们看到的LAZY对象时一个初始化完成后的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值