barrier cream是什么意思 barrier( 二 )


In computer science, the happened-before relation is a relation between the result of two events, such that if one event should happen before another event, the result must reflect that, even if those events are in reality executed out of order (usually to optimize program flow).
翻译结果如下:
在计算机科学中,先行发生原则是两个事件的结果之间的关系,如果一个事件发生在另一个事件之前,结果必须反映,即使这些事件实际上是乱序执行的(通常是优化程序流程) 。
【barrier cream是什么意思 barrier】这里所谓的事件,实际上就是各种指令操作,比如读操作、写操作、初始化操作、锁操作等等 。
先行发生原则作用于很多场景下,包括同步锁、线程启动、线程终止、volatile 。我们这里只列举出volatile相关的规则:
对于一个volatile变量的写操作先行发生于后面对这个变量的读操作 。
回到上述的代码例子,如果在静态变量s之前加上volatile修饰符:
volatile static int s = 0;
线程A执行如下代码:
s = 3;
这时候我们引入线程B,执行如下代码:
System.out.println("s=" + s);
当线程A先执行的时候,把s = 3写入主内存的事件必定会先于读取s的事件 。所以线程B的输出一定是s = 3 。
那么,volatile关键字就可以保证线程安全了么?并不是,volatile只能保证变量的可见性,并不能保证变量的原子性 。举个栗子:
barrier cream是什么意思 barrier

文章插图
这段代码是什么意思呢?很简单,开启10个线程,每个线程当中让静态变量count自增100次 。执行之后会发现,最终count的结果值未必是1000,有可能小于1000 。
使用volatile修饰的变量,为什么并发自增的时候会出现这样的问题呢?这是因为count++这一行代码本身并不是原子性操作,在字节码层面可以拆分成如下指令:
getstatic //读取静态变量(count)
iconst_1 //定义常量1
iadd //count增加1
putstatic //把count结果同步到主内存
虽然每一次执行 getstatic 的时候,获取到的都是主内存的最新变量值,但是进行iadd的时候,由于并不是原子性操作,其他线程在这过程中很可能让count自增了很多次 。这样一来本线程所计算更新的是一个陈旧的count值,自然无法做到线程安全:
barrier cream是什么意思 barrier

文章插图

barrier cream是什么意思 barrier

文章插图

barrier cream是什么意思 barrier

文章插图

barrier cream是什么意思 barrier

文章插图
因此,什么时候适合用volatile呢?
1.运行结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值 。
2.变量不需要与其他的状态变量共同参与不变约束 。
第一条很好理解,就是上面的代码例子 。第二条是什么意思呢?可以看看下面这个场景:
volatile static int start = 3;
volatile static int end = 6;
线程A执行如下代码:
while (start < end){
//do something
}
线程B执行如下代码:
start+=3;
end+=3;
这种情况下,一旦在线程A的循环中执行了线程B,start有可能先更新成6,造成了一瞬间 start == end,从而跳出while循环的可能性 。
什么是指令重排?
指令重排是指JVM在编译Java代码的时候,或者CPU在执行JVM字节码的时候,对现有的指令顺序进行重新排序 。
指令重排的目的是为了在不改变程序执行结果的前提下,优化程序的运行效率 。需要注意的是,这里所说的不改变执行结果,指的是不改变单线程下的程序执行结果 。
然而,指令重排是一把双刃剑,虽然优化了程序的执行效率,但是在某些情况下,会影响到多线程的执行结果 。我们来看看下面的例子:
boolean contextReady = false;
在线程A中执行:
context = loadContext();
contextReady = true;
在线程B中执行:
while( ! contextReady ){
sleep(200);
}
doAfterContextReady (context);
以上程序看似没有问题 。线程B循环等待上下文context的加载,一旦context加载完成,contextReady == true的时候,才执行doAfterContextReady 方法 。

秒懂生活扩展阅读