美文网首页
Happens-Before

Happens-Before

作者: 竖起大拇指 | 来源:发表于2022-08-30 13:33 被阅读0次

    1.什么是happens-before

    happens-before:A happens-before B就是A先行发生于B(这种说法不是很准确),定义为hb(A, B)。在Java内存模型中,happens-before的意思是前一个操作的结果可以被后续操作获取。

    2.有哪些happens-before规则

    class Example {
    
        var x = 0
    
        @Volatile
        var v: Boolean = false
    
        fun write() {
            x = 42
            v = true
        }
    
        fun reader() {
            if (v) {
                println(x)
            }
        }
    
    }
    

    这里,假设线程A执行writer()方法,按照volatile会将v=true写入内存;线程B执行reader()方法,按照volatile,线程B会从内存中读取变量v,如果线程B读取到的变量v为true,那么,此时的变量x的值是多少呢??

    这个示例程序给人的直觉就是x的值为42,其实,x的值具体是多少和JDK的版本有关,如果使用的JDK版本低于1.5,则x的值可能为42,也可能为0。如果使用1.5及1.5以上版本的JDK,则x的值就是42。

    看到这个,就会有人提出问题了?这是为什么呢?其实,答案就是在JDK1.5版本中的Java内存模型中引入了Happens-Before原则。

    接下来,我们就结合案例程序来说明Java内存模型中的Happens-Before原则。

    【原则一】程序次序规则:在一个线程内一段代码的执行结果是有序的。就是还会指令重排,但是随便它怎么排,结果是按照我们代码的顺序生成的不会变。

    例如【示例】中的程序x=42会在v=true之前执行。这个规则比较符合单线程的思维:在同一个线程中,程序在前面对某个变量的修改一定是对后续操作可见的。

    【原则二】volatile变量规则:就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。
    【原则三】传递规则:如果A Happens-Before B,并且B Happens-Before C,则A Happens-Before C。

    我们结合【原则一】、【原则二】和【原则三】再来看【示例】程序,此时,我们可以得出如下结论:

    • x = 42 Happens-Before 写变量v = true,符合【原则一】程序次序规则。
    • 写变量v = true Happens-Before 读变量v = true,符合【原则二】volatile变量规则。
      再根据【原则三】传递规则,我们可以得出结论:x = 42 Happens-Before 读变量v=true。

    也就是说,如果线程B读取到了v=true,那么,线程A设置的x = 42对线程B就是可见的。换句话说,就是此时的线程B能够访问到x=42。

    【原则四】锁定规则:对一个锁的解锁操作 Happens-Before于后续对这个锁的加锁操作。
    public class Test{
    
       private int x=0;
    
        public void initx(){
            synchronized(this){  //自动加锁
                if(x<10){
                    x=10
                }
            } //自动释放锁
        }
    }
    

    我们可以这样理解这段程序:假设变量x的值为0,线程A执行完synchronized代码块之后将x变量的值修改为10,并释放synchronized锁。当线程B进入synchronized代码块时,能够获取到线程A对x变量的写操作,也就是说,线程B访问到的x变量的值为10。

    【原则五】线程启动规则:在主线程A执行过程中,启动子线程B,那么线程A在启动子线程B之前对共享变量的修改结果对线程B可见。

    我们也可以这样理解:如果线程A调用线程B的start()方法来启动线程B,则start()操作Happens-Before于线程B中的任意操作。

    fun main2(args: Array<String>) {
            var x = 1
            //在主线程A中初始化线程B
            var thread = Thread {
                println("x=${x}")  //此处的变量值是多少了? 100
              
            }
            //主线程在启动子线程B之前 将变量修改为100
             x = 100
            thread.start()
        }
    

    上述代码是在线程A中执行的一个代码片段,根据【原则五】线程的启动规则,线程A启动线程B之后,线程B能够看到线程A在启动线程B之前的操作,在线程B中访问到的x变量的值为100。

    【原则六】线程终结规则:线程A等待线程B完成(在线程A中调用线程B的join()方法实现),当线程B完成后(线程A调用线程B的join()方法返回),则线程A能够访问到线程B对共享变量的操作。

    也可以这样理解:在主线程A执行过程中,子线程B终止,那么线程B在终止之前对共享变量的修改结果在线程A中可见。也称线程join()规则。

     fun main2(args: Array<String>) {
            var x = 1
            var thread = Thread {
               x=100  //在线程B中将共享变量修改为100
            }
            thread.start() //启动线程
            thread.join() //等待线程执行完毕
            println(x)
        }
    

    【原则七】线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,可以通过Thread.interrupted()检测到是否发生中断。

    例如,下面的程序代码。在线程A中中断线程B之前,将共享变量x的值修改为100,则当线程B检测到中断事件时,访问到的x变量的值为100。

       fun main2(args: Array<String>) {
            var x = 1
            var thread = Thread {
                println("x=${x}")
                if (Thread.currentThread().isInterrupted) {
                    println("interrupt x=${x}")
    
                }
            }
            thread.start()
            x = 100
    
            thread.interrupt()
            println(x)
        }
    

    【原则八】对象终结原则:这个也简单的,就是一个对象的初始化的完成,也就是构造函数执行的结束一定 happens-before它的finalize()方法。

    相关文章

      网友评论

          本文标题:Happens-Before

          本文链接:https://www.haomeiwen.com/subject/rhhdnrtx.html