深入理解重排序

</pre>理解一下数据依赖性<p></p><p><strong>  </strong> 如果有两个操作,访问同一个变量并且有一个操作是写操作,那这两个操作存在数据依赖。</p><p>    一般有如下几种情况:</p><p>     1.写后写:a=1;a=2;</p><p>     2.写后读:a=1;b=a;</p><p>     3.读后写:b=a;a=1;</p><p>如上几种情况都存在依赖关系。存在依赖关系的操作就无法重排序。重排序会导致结果不一致。</p><p>例子:</p><pre name="code" class="java"></pre><pre name="code" class="java">if(flag){//1
  a=a+1;//2
}
 上述例子是否存在数据依赖,是否可以重排序?

 答案:根据上述的数据以来的概念,1根2不存在数据以来可以重排序。why?

              因为如果重排序了,编译器和处理器会采用中间变量来管理这两行代码。先执行2 ,mod = a+1; 在执行1,如果flag=true,则继续a=mod.把计算的过程提前处理了。

前面有提到过几种重排序方法,有编译器重排序,指令级重排序,内存系统重排序,其中2跟3属于处理器重排序。编译器和处理器都会根据数据依赖进行重排序。

    注意一点,这边所说的数据依赖是真对单个处理器和单个内存指令,不同处理器不同线程的数据依赖,处理器和编译器考虑不到。

   所以,重排序会在单线程中保持一致的结果(as -if -serial)。而多线程则不保证结果一致。

as -if -serial语义

  as -if -serial语义的意思是不管怎么重排序(为了提高处理器和编译器的性能和并发度),程序的执行结果都是一致的,当然前提是在一个单线程内。编译器和处理器也都遵循这个规则。

   举个栗子为什么存在数据依赖的不能进行重排序。

    a = 2;//A

    b = 3;//B

    c = a+b;//C

上面ABC的依赖关系是AC,BC存在依赖关系,所以C跟AB不能进行重排序,否则会影响结果。所以C只能在AB的后面执行。但是AB之间却不存在数据以来关系,所以执行的顺序可能是ABC ,BAC这两种情况。

  as -if -serial语义把单线程重排序的结果保护起来,让程序可以执行到一致的结果。

多线程中为什么不可以重排序?

我们来分析一个例子。

public class Test {
	int a = 0;
	boolean flag =  false;
	
	public void A(){
		a=1;//1
		flag=true;//2
	}
	
	public void B(){
		if(flag){//3
			a=a+1;//4
		}
	}

}
如上代码,假如我们有两个线程一个执行A方法,一个执行B方法,如果A方法因为两个操作都不存在数据依赖,所以先执行2(flag=true),然后这个时候如果程序片跳到T2,这个时候开始执行3跟4,a的结果就等于1,而按照正常的逻辑应该是a=2.所以多线程破坏了as-if-serial语义。

依据重排序,还有多种重排序的执行顺序,3根4也可以进行重排序,4的值先存在一个临时中间值。

所以在单线程中,重排序不会影响语义。而多线程则会影响重排序的语义,会导致结果不一致。




了解两个知识点

原子操作:原子操作是指不会被线程调度打断的操作,这种操作一旦开始就会一直运行到结束,中间没有没有context weitch.通俗的讲就是只有一个操作,不能打断的操作。例子:

 int a = 1;//原子操作,一旦执行到这个语句,就只能全部执行完

a=a++;//不是原子操作

a++可以分成三个步骤

1.读取a

2.a+1

3.再把a+1赋值到a



long和double的类型是和进行存储的是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值