【多线程:volatile】有序性
01.介绍
什么是有序性
在不改变程序结果的前提下 指令可以在各个阶段通过重排序和组合来实现指令级并行。
注意:
重排序指令不能影响结果
为什么这样做
cpu处理过程
在cpu处理一个指令是包含一系列工序的,包含了:取指 译码 地址生成 执行 回写,这五步 但是这五步之间是可以交错运行的,也就是一个指令译码的时候另一个指令可以取指。
例子1
x=a;
y=b;
这样的执行效率显然更加的快,且在语句之间没有关系时 执行顺序可以随便,也就是可能先执行y=b 再执行x=a,不影响结果
例子2
x=a;
c=x+1;
y=b;
这段代码是否可以套用上述的图片,是不行的。
理论上应该对应的是下面这幅图
但实际上这段代码被优化后可能是
x=a;
y=b;
c=x+1;
对应的图片
这样做的原因是c=x+1需要x的值确定 也就是一定要等待x的指令全部完成之后才能执行c的指令,但是按照最初的顺序y=b是在最后的 也就是他也需要在x指令执行完才能运行 但是y=b和前两个指令没有任何关系,所以进行了优化 使得在x运行期间 y也可以运行,提高了并发性,使单独一个指令运行的情况减少。
总结:例子二的这种情况就是指令重排序,在单线程情况下没有什么影响,但是在多线程情况下就有可能出问题。
02.多线程情况下的指令排序
public class Disorder {
private static int x=0,y=0;
private static int a=0,b=0;
//无论这两个线程怎么排列组合 如果没有重排序,一定不会存在x=0 且 y=0
public static void main(String[] args) throws InterruptedException {
int i=0;
//不停的死循环
while (true){
i++;
x=0;y=0;
a=0;b=0;
Thread one= new Thread(()-> {
//前后没有依赖关系 a=1和 x=b
a=1;
x=b;
});
Thread other = new Thread(()-> {
//如果不存在乱序,b=1一定在y=a前面
b=1;
y=a;
});
one.start();other.start();
one.join();other.join();
String result = "第"+i+"次("+x+","+y+")";
if(x==0 && y==0){
System.err.println(result);
break;
}else{
System.out.println(result);
}
}
}
public static void shortWait(long interval){
long start = System.nanoTime();
long end;
do{
end=System.nanoTime();
}while (start+interval>=end);
}
}
结果
.
.
.
.
第181799次(0,1)
第181800次(0,1)
第181801次(0,1)
第181802次(0,1)
第181803次(0,1)
第181804次(0,0)
解释
我们分析上述代码,考虑到多线程的情况,理论上x,y最多组合的情况就只有3种(1,0) (1,1) (0,1),但是我们却发现了第四种情况(0,0) 要出现这种情况一定是x=b在a=1之前,y=a在b=1之前,很显然这种情况就只有指令重排序了
03.禁止指令重排序
很简单就是在x,y前加一个volatile,作用是保证在x,y之前的代码都比它先执行,例如a=1;x=b;保证a=1一定在x=b之前执行,同理y也是这样,同时把之前的变量包括自己的值同步到主存中。同时需要给a,b前加上volatile保证a b的可见性 要不然可能会出现a b已经更新到主存 但读取的还是本地内存
补充:最后这段解释可能大家会理解的不清楚,具体解释涉及到volatile的原理,下一个文章会讲。