重排序和数据依赖性
作者:互联网
本文首发于微信公众号【猿灯塔】,转载引用请说明出处
今天是猿灯塔“365天原创计划”第8天。
今天讲:
什么是重排序?
重排序是指编译器和处理器为了优化程序性能对指令序列进行重新排序的一种手段。Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序可以保证最终执行的结果是与程序顺序执行的结果一致,并且只会对不存在数据依赖性的指令进行重排序,这个重排序在单线程下对最终执行结果是没有影响的,但是在多线程下就会存在问题。
1int a = 1;(1)
2int b = 2;(2)
3int c= a + b;(3)
如上c的值依赖a和b的值,所以重排序后能够保证(3)的操作在(2)(1)之后,但是(1)(2)谁先执行就不一定 了,这在单线程下不会存在问题,因为并不影响最终结果。
1class RecorderExample{
2int a = 0;
3boolean flag = false;
4public void write(){
5a = 1; // 1
6flag = true; // 2
7}
8public void read(){
9if(!flag){ // 3
10a = 2; // 4
11}
12}
13}
有两个线程,线程A首先执行write()方法,线程B首先执行read()方法。
JMM中可能的执行时序为:
而编译器可以对操作1和操作2进行重排序
得到的执行时序为:
一个多线程的例子:
1public class TestCP {
2private static int num =0;
3private static boolean ready = false;
4public static class ReadThread extends Thread {
5public void run() {
6while(!Thread.currentThread().isInterrupted()){
7if(ready){//(1)
8System.out.println(num+num);//(2)如代码由于(1)(2)(3)(4) 之间不存在依赖,所以写线程(3)(4)可能被重排序为先执行(4)在执行(3),那么
9执行(4)后,读线程可能已经执行了(1)操作,并且在(3)执行前开始执行(2)操作,这时候打印
10结果为0而不是4.
11改变程序执行结果的直接原因是重排序,而根本原因是未正确同步。
12如果程序是正确同步的,程序的执行将具有顺序一致性。即:正确同步程序的执行结果与顺序一致性内
13存模型的执行结果相同。
14注意:这里的同步是广义的同步,包括同步原语(synchronized,volatile,fifinal)的正确使用。
15从源代码到最终实际执行的指令序列,会分别经历以下三中排序
162、数据依赖性
17如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖
18性。数据依赖分为下列3种类型,如下图所示 19}
20System.out.println("read thread....");
21}
22}
23}
24public static class Writethread extends Thread {
25public void run() {
26num = 2;//(3)
27ready = true;//(4)
28System.out.println("writeThread set over...");
29}
30}
31public static void main(String[] args) throws InterruptedException {
32ReadThread rt = new ReadThread();
33rt.start();
34Writethread wt = new Writethread();
35wt.start();
36Thread.sleep(10);
37rt.interrupt();
38System.out.println("main exit");
39}
40}
如代码由于(1)(2)(3)(4)之间不存在依赖,所以写线程(3)(4)可能被重排序为先执行(4)在执行(3)那么执行(4)后,读线程可能已经执行了(1)操作,并且在(3)执行前开始执行(2)操作,这时候打印结果为0而不是4。
改变程序执行结果的直接原因是重排序,而根本原因是未正确同步。
如果程序是正确同步的,程序的执行将具有顺序一致性。即:正确同步程序的执行结果与顺序一致性内存模型的执行结果相同,注意:这里的同步是广义的同步,包括同步原语(synchronized,volatile,fifinal)的正确使用,从源代码到最终实际执行的指令序列,会分别经历以下三中排。
数据依赖性
如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。
数据依赖分为下列3种类型如下图所示:
上面3种情况,只要重排序两个操作的执行顺序,程序的执行结果就会被改变。前面提到过编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。
365天干货不断,可以微信搜索「 猿灯塔」第一时间阅读,回复【资料】【面试】【简历】有我准备的一线大厂面试资料和简历模板
标签:排序,依赖性,编译器,同步,线程,操作,执行,数据 来源: https://www.cnblogs.com/yuandengta/p/12710660.html