Java中happen-before
2016-05-06 16:52
225 查看
Happen-Before定义:即 A操作的结果对B操作是可见的,或者说是B操作中如果用到A操作涉及到的数据的结果,A操作一定已经完成,并会影响到B操作。
JMM中定义的happen-before规范如下:
1、程序次序规则:在一个单独的线程中,按照程序代码的执行流顺序,(时间上)先执行的操作happen—before(时间上)后执行的操作。
2、管理锁定规则:一个unlock操作happen—before后面(时间上的先后顺序,下同)对同一个锁的lock操作。
3、volatile变量规则:对一个volatile变量的写操作happen—before后面对该变量的读操作。(如果前面已经读过的它不再保证)
4、线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。
5、线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
6、线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。
7、对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。
8、传递性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。
疑问:代码执行时间上的先后和happen-before的关系怎样呢?
1.操作A先于操作B发生并不意味这操作A happen-before操作B,例如:class TestDemo{
private int value = 0;
public int get(){
return value;
}
public void set(int value){
this.value = value;
}
}如果thread_1 进行get操作,thread_2进行set操作,如果set在时间上先执行(结果被放在了cache中并没有及时write到主存),get在时间上后执行(还是拿的历史结果),根据上面8条规则,没有符合的规则,所以完全有可能拿不到set操作的结果。
2.操作A happen-before 操作B也不意味着操作A先于B执行:
例如:
int x = 1;
int y = 2;
假设同一个线程执行上面两个操作:操作A:x=1和操作B:y=2。根据happen—before规则的第1条,操作A happen—before
操作B,但是由于编译器的指令重排序(Java语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。这个过程通过叫做指令的重排序。指令重排序存在的意义在于:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能。在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整)等原因,操作A在时间上有可能后于操作B被处理器执行,但这并不影响happen—before原则的正确性。
最后,一个操作和另一个操作必定存在某个顺序,要么一个操作或者是先于或者是后于另一个操作,或者与两个操作同时发生。同时发生是完全可能存在的,特别是在多CPU的情况下。而两个操作之间却可能没有happen-before关系,也就是说有可能发生这样的情况。两个存在happen-before关系的操作不可能同时发生,一个操作A
happen-before操作B,它们必定在时间上是完全错开的,这实际上也是同步的语义之一(独占访问)。
JMM中定义的happen-before规范如下:
1、程序次序规则:在一个单独的线程中,按照程序代码的执行流顺序,(时间上)先执行的操作happen—before(时间上)后执行的操作。
2、管理锁定规则:一个unlock操作happen—before后面(时间上的先后顺序,下同)对同一个锁的lock操作。
3、volatile变量规则:对一个volatile变量的写操作happen—before后面对该变量的读操作。(如果前面已经读过的它不再保证)
4、线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。
5、线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
6、线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。
7、对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。
8、传递性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。
疑问:代码执行时间上的先后和happen-before的关系怎样呢?
1.操作A先于操作B发生并不意味这操作A happen-before操作B,例如:class TestDemo{
private int value = 0;
public int get(){
return value;
}
public void set(int value){
this.value = value;
}
}如果thread_1 进行get操作,thread_2进行set操作,如果set在时间上先执行(结果被放在了cache中并没有及时write到主存),get在时间上后执行(还是拿的历史结果),根据上面8条规则,没有符合的规则,所以完全有可能拿不到set操作的结果。
2.操作A happen-before 操作B也不意味着操作A先于B执行:
例如:
int x = 1;
int y = 2;
假设同一个线程执行上面两个操作:操作A:x=1和操作B:y=2。根据happen—before规则的第1条,操作A happen—before
操作B,但是由于编译器的指令重排序(Java语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。这个过程通过叫做指令的重排序。指令重排序存在的意义在于:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能。在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整)等原因,操作A在时间上有可能后于操作B被处理器执行,但这并不影响happen—before原则的正确性。
最后,一个操作和另一个操作必定存在某个顺序,要么一个操作或者是先于或者是后于另一个操作,或者与两个操作同时发生。同时发生是完全可能存在的,特别是在多CPU的情况下。而两个操作之间却可能没有happen-before关系,也就是说有可能发生这样的情况。两个存在happen-before关系的操作不可能同时发生,一个操作A
happen-before操作B,它们必定在时间上是完全错开的,这实际上也是同步的语义之一(独占访问)。
相关文章推荐
- 关于android WebViewClient和WebChromeClient
- 关于小米文件管理器的介绍及源码下载
- Swift -- 5.集合
- andorid studio 配置NDK环境
- android 项目练习:自己的词典app——生词本(一)
- 浅谈iOS如何做弹幕
- Android - 文件读写操作 总结
- swift 柯里化
- Android布局
- iOS上架之内购
- Android LayoutInflater深度解析 给你带来全新的认识
- Android-获取应用程序列表
- Android ListView单选CheckBox
- Android开发调用第三方邮件应用发送邮件
- 最全面的AndroidStudio配置指南总结-包括护眼模式
- 最全面的AndroidStudio配置指南总结-包括护眼模式
- iOS编码
- 微信开发-SHA1算法
- Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED解决方法
- iOS 8 之后的动态沙盒路径