栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

并发编程必知必会——Happens-before

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

并发编程必知必会——Happens-before

一段代码告诉你什么是并发编程的bug
public class ReorderExample {

    private int x = 0;
    private int y = 1;
    private boolean flag = false;


    public void writter() {
        x = 42;
        y = 50;
        flag = true;
    }


    public void reader() {
        if (flag) {
            System.out.println("x=" + x + " y=" + y);
        }
    }


}

可以看到上面这段代码,writter负责写,reader负责读,单线程情况下顺序执行这两个方法没有问题。多线程情况下,线程1执行写,线程2执行读会发生什么呢?请看下图:


为了保证程序运行的效率,编译器常常会擅自优化语句,线程1执行的writter很可能会优化成上图左边所示代码,在多线程情况下,假如执行到flag=true,直接切换到线程2,这就会导致预期输出的x,y应该是42,50,结果却是42,1。

解决方案:将flag声明语句中加一个volatile

public class ReorderExample {

    private int x = 0;
    private int y = 1;
    private volatile boolean flag = false;


    public void writter() {
        x = 42;  //代码1
        y = 50; //代码2
        flag = true;//代码3
    }


    public void reader() {
        if (flag) {//代码4
            System.out.println("x=" + x + " y=" + y);//代码5
        }
    }


}

你一定被我的操作搞得一脸懵逼,为什么这样一个关键字就能避免指令重排呢?这时候我就可以给你好好掰扯掰扯大名顶顶的happens-before原则了。


这样一个表格是不是直接给你干懵了?其实就可以翻译成一句人话:只要第一个操作是volatile读,或第二个操作volatile写就不会发生指令重排。

以上面的代码为例,代码1和代码2只是普通写(可以把这两个操作当作普通读/写,他们随意指令重排对结果没有任何影响),而代码3为volatile写(第二个操作),根据上面那句人话:“第二个操作volatile写就不会发生指令重排”。
代码4是volatile读,代码5是普通读写,根据上面的人话:“只要第一个操作是volatile读,就不会发生指令重排”

这样就保证了代码的有序性。那么可见性呢?请看笔者画的又一张图。


可以看到加了volatile关键字的线程1保证了在他前面的操作永远不会到他后面,如果我们把代码1、代码2看作一个代号为A的操作,再把flag=true看作代号为B的操作,线程2的if(flag)看作代号C的操作的话。再结合表格我们可以看出:

A是普通写,B是volatile写,那么A就happens-before于B。

而C是volatile读,根据表格可以看出:

B是volatile写,C是volatile读,他们也不会发生指令重排,那么B就happens-before于C。

经过这样一段推倒,我们就可以得到Happends-before的有一个废话原则,那就是传递性规则:

 A happens-before B, 且 B happens-before C, 那么 A happens-before C

这样就保证了可见性,至此打完收工

详述happends-before其他守则 监视器锁规则

对⼀个锁的解锁 happens-before 于随后对这个锁的加锁

这也是一句废话,后来人上锁必须等前人解锁才行,所以后来人加锁时的对你的操作永远是可见的。就以下面代码为例,你每次上锁、解锁后x变为多少你永远都知道的。

public class SynchronizedExample {

    private int x = 0;


    public void synBlock() {
// 1.加锁
        synchronized (SynchronizedExample.class) {
            x = 1; // 对x赋值
        }
// 3.解锁
    }

    // 1.加锁
    public synchronized void synMethod() {
        x = 2; // 对x赋值
    }
// 3. 解锁
}
start()规则
package com.example.volatileOpr;

public class StartExample {


    private int x = 0;
    private int y = 1;
    private  boolean flag = false;


    public void reader() {
        if (flag) {//代码4
            System.out.println("x=" + x + " y=" + y);//代码5
        }
    }


    public static void main(String[] args) {
        StartExample startExample = new StartExample();

        Thread thread = new Thread(startExample::reader);

        startExample.x = 0;
        startExample.y = 0;
        startExample.flag=true;
        
        
        //子线程start在主线程一顿操作后start,那么他就能看到start的操作就对他可见
        thread.start();//x=0 y=0
        System.out.println("主线程结束");
    }
}

说白了,就是主线程在子线程start前的任何操作,子线程都能看到,翻译成鬼话就是:

如果线程 A 执⾏操作 ThreadB.start() (启动线程B), 那么 A 线程的

ThreadB.start() 操作 happens-before 于线程 B 中的任意操作join()规则

package com.example.volatileOpr;

public class JoinExample {


    private int x = 0;
    private int y = 1;
    private  boolean flag = false;


    public void writter() {
        x = 42;  //代码1
        y = 50; //代码2
        flag = true;//代码3
    }


    public static void main(String[] args) throws InterruptedException {
        JoinExample joinExample = new JoinExample();

        Thread thread = new Thread(joinExample::writter);


        thread.start();

        //加了join后主线程永远需要等到子线程结束后才能结束,这意味着子线程所有的操作对主线程都是可见的
        thread.join();
        System.out.println("主线程结束 x=" + joinExample.x + " y=" + joinExample.y);//x=42 y=50
    }
}

join规则和start规则差不多,说白了就是子线程用了join后所有的操作都对主线程可见,翻译成鬼话就是:
如果线程 A 执⾏操作 ThreadB.join() 并成功返回, 那么线程 B 中的任意操作happens-before 于线程 A 从 ThreadB.join() 操作成功返回
打完收工,欢迎来笔者的源码地址

https://gitee.com/fugongliudehua/concurrency

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/735617.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号