DCL的单例模式,有了synchronized关键字,为何还需要加volatile关键字?

问题

DCL(双重检验锁)式单例模式,加了sychronized关键字后,为何还需要加volatile关键字?

关键点

指令重排

单例源码

/**
 * Created by lerry on 2017/9/21.
 * @author lerry
 */
public class SingletonDCL {
	private volatile static SingletonDCL singleton;

	private static int counter = 0;

	private SingletonDCL() {
		counter++;
		System.out.println(String.format("构造对象被调用[%d]次", counter));
	}

	/**
	 * 双重校验锁式(也有人把双重校验锁式和懒汉式归为一类)分别在代码锁前后进行判空校验,
	 * 避免了多个有机会进入临界区的线程都创建对象,
	 * 同时也避免了后来线程在"lazythreadsafe"中,先来线程创建对象后,但仍未退出临界区的情况下等待
	 * @return
	 */
	public static SingletonDCL getInstance() {
		// 模拟同步方法的耗时  start
		try {
			System.out.println(String.format("[%s]获取对象实例等待1秒", Thread.currentThread().getName()));
			Thread.sleep(1000);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 模拟同步方法的耗时  end
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null) {
					singleton = new SingletonDCL();
				}// end if
			}// end syn
		}// end if
		return singleton;
	}
}

解释验证

我们创建一个简单的类T,里面有一个私有成员变量orderCount,默认值为100
编译成.class之后,使用jclasslib查看main方法。
T的字节码

图:T字节码

 new #2 <com/hua/jvm/class01/T>

会为T分配内存空间。这时会为成员变量附加初始值,为0

invokespecial #3 <com/hua/jvm/class01/T.<init>>

会对T的成员变量进行初始化,把值附为100

 astore_1

100指向t
按顺序执行,是没有问题的。
但是,当发生指令重排,CPU乱序执行时,可能会先执行第4行代码,把0附加给t。这时,内存空间已分配,单例对象不再是null,有新的线程来访问时,就可以访问到这个对象, 然后把订单数量0给拿走了。 这时CPU再执行第3行代码,给成员变量附100的值。发生了线程安全问题。
相当于在对象半初始化时,有新线程拿到了半初始化的对象,发生线程安全问题。

而添加了volatile之后,可以保证可见性有序性,避免了此类的线程安全问题。

参考

DCL单例模式为什么还需要加volatile

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值