从单例模式挖到内存模型(二)----指令重排序

首先是一个双检锁写的单例模式的例子:

public class Single{
	private volatile static Single single;
	private Single(){}
	public static Single getInstance(){
		if(single==null){
			synchronized (Single.class) {
				if(single==null){
					single=new Single();
				}
			}
		}
		return single;
	}
}


下面分析一下指令重排序(也有名字叫乱序执行,无序写入)给这个单例模式带来的问题:

要分析上面例子中存在的问题,就要从instance = new Singleton()这句开始,对java来说,创建新的对象并不是一个原子操作,这个过程分成了3步:

1,给 instance 分配内存

2,调用 Singleton 的构造函数来初始化成员变量

3,将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)


关键:

1,在JVM的即时编译器中,存在一个设定,叫做指令重排序。

2,在上面的例子中,2操作依赖1操作,但3操作并不依赖2操作,也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是1-2-3 也可能是1-3-2。如果是后者,则在3执行完毕,2未执行之前,被线程二抢占了,这时instance已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

3,JDK1.5以后,因为内存模型的优化,上面的例子不会再因为指令重排序而出现问题。


关于指令重排序的说明:

1,JVM为了使得处理器内部的运算单元能充分利用,使效率最大化,处理器可能会对输入代码进行指令重排序的优化,处理器会在计算之后将乱序执行的结果进行重组,保证该结果与顺序执行的结果是一样的,但并不保证程序中各个语句计算的先后顺序与输入的代码顺序一致(这种保证一致的原则叫做as-if-serial)。

2,在多线程的情况下,指令的重排序可能会影响计算的结果。

3,如果java认为两个操作有数据依赖性,则不会重排序。 


重排序有三种,在某一次编译的过程中,这三种重排序的情形有可能都出现:

1,编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

2,指令级并行的重排序:如果不存l在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

3,内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值