Java指令重排和happens-before规则

指令重排

重排序:Java 语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。这个过程叫做指令的重排序。

指令重排序存在的意义在于:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能。

重排序的种类

编译期重排:编译源代码时,编译器依据对上下文的分析,对指令进行重排序,以之更适合于CPU的并行执行。

运行期重排:CPU在执行过程中,动态分析依赖部件的效能,对指令做重排序优化。

内存重排:程序执行一段代码,写一个普通的共享变量,其可能先被写到缓冲区然后再被写到主内存,此时指令完成的时间就被推迟了。实际表现就是内存重排。

	public int multiply(int number , int multiplier){
		int number1;
		int multiplier1;
		number1=number;//A
		multiplier1=multiplier;//B
		int result=number1*multiplier1;//C
		return result;
	}

上面的操作在运行之前编译器和处理器可能会进行优化,产生如下结果:

	public int multiply(int number , int multiplier){
		int number1;
		int multiplier1;
		multiplier1=multiplier;//B
		number1=number;//A
		int result=number1*multiplier1;//C
		return result;
	}

AB进行了重排,但是,这种重排序不会影响结果,因为它遵循了happens-before规则,但是如果进行下面的重排序(AC进行了重排,在实际上是不会发生的,因为不符合happens-before规则)就会影响结果

public int multiply(int number , int multiplier){
		int number1;
		int multiplier1;
		multiplier1=multiplier;//B
		int result=number1*multiplier1;//C
		number1=number;//A
		return result;
	}

happens-before规则

happens-before规则:是用来判断一个操作的结果对另一个操作是否可见的法则(不管这两个操作是不是在同一个线程)。如果A happens-before B 那么A操作的结果对B来说是可见的。

如果两个操作之间没有happens-before关系,那么JVM可以对它们进行任意重排序;如果两个操作之间有happens-before关系,那么JVM在对它们进行重排序时依照happens-before规则进行排序。

程序员可以基于happens-before规则知道某线程的操作对另一个线程是否可见,进行可见性的判断。

具体规则
  • 程序顺序规则:一个线程中的A操作,happens-before 于该线程中的任意后续B操作。A happens-before B。

  • 监视器锁规则:在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前执行。监视器的解锁happens-before于每一个后续对同一个监视器的加锁。

  • volatile变量规则:对volatile变量的写入操作必须在对该变量的读操作之前执行。对一个volatile变量的写操作,happens-before后续对这个变量的读操作。

  • 线程启动规则:在线程上对Thread.Start的调用必须在该线程中执行任何操作之前执行。主线程A启动线程B,线程B中可以看到主线程启动B之前的操作。也就是start() happens-before 线程B中的操作。

  • 线程结束规则:线程中的任何操作都必须在其他线程检测到该线程已经结束(或者在其他线程从Threadjoin中成功返回,或者在其他线程在调用ThreadisAlive时返回false)之前执行。主线程A等待子线程B完成,当子线程B执行完毕后,主线程A可以看到线程B的所有操作。也就是说,子线程B中的任意操作 happens-before join()的返回。

  • 中断规则:当一个线程在另一个线程上调用interrupt时,必须在被中断线程检测到interrupt调用之前执行(通过抛出InterruptedException,或者调用isInterrupted和interrupted)。一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。

  • 终结器规则:对象的构造函数必须在启动该对象的终结器之前执行完成。一个对象构造函数的结束happens-before与该对象的finalizer的开始。

  • 传递性:如果操作A在操作B之前执行,并且操作B在操作C之前执行,那么操作A必须在操作C之前执行。如果A happens-before B ,而B happens-before C,那么A happens-before C 。

值得注意的是两个操作之间具有A happens-before B 并不意味着A操作必须要在B操作之前执行!,而是操作A的结果对于操作B是可见的。

happens-before规则可以保证可见性,不同的happens-before规则对实现可见性有不同的方法。这个有两个例子Java 中synchronized关键字及volatile的可见性实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值