[随笔-1]ThreadX.join().并发修改能及时被观测的神技

50 阅读2分钟
import javax.swing.text.html.StyleSheet;
import java.util.*;

public class Main{
    private int x = 0;
    private int y = 0;
    private boolean flag = false;
    public static void main(String[] args) throws InterruptedException{
        Thread.currentThread().setName("线程A");
        Main main = new Main();
        Thread threadB = new Thread(main::writer,"线程B");
        threadB.start();
        
        System.out.println("x的值"+main.x);
        System.out.println("Y的值"+main.y);
        System.out.println("flag:"+main.flag);
        System.out.println("线程结束");
    }
    public void writer(){
        System.out.println("开始赋值操作");
        this.x = 100;
        this.y = 200;
        this.flag = true;
    }
}

就上述的代码而言,似乎没有太大的问题。我们启动了一个新的线程,新的线程会对x和y进行赋值。然后我们在赋值之后回到主线程,在主线程打印出x和y与flag的值。

但是我们尝试运行会发现:

D:\A-EssentialSoftWare\Java\jdk-17\bin\java.exe -Dvisualvm.id=52508452716600 "-javaagent:D:\A-EssentialSoftWare\All-Kinds-Of-IDE\IntelliJ IDEA 2024.1.2\lib\idea_rt.jar=58688:D:\A-EssentialSoftWare\All-Kinds-Of-IDE\IntelliJ IDEA 2024.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\A-ProgramingTask\JavaProgrammingTask\TestJavaSE\out\production\TestJavaSE Main
开始赋值操作
x的值0
Y的值200
flag:true
线程结束

这是为什么?

经过分析我们发现:

由于ThreadB与线程A是并行的关系。在ThreadB中发生了指令的重排序,y先被赋值。同时线程A在x赋值之前输出了四条System语句

那么解决策略是什么?

我们只要等线程B执行完之后,再让线程A继续运行即可

那怎么实现呢?

在线程A中 以线程B为主体调用join即可

import javax.swing.text.html.StyleSheet;
import java.util.*;

public class Main{
    private int x = 0;
    private int y = 0;
    private boolean flag = false;
    public static void main(String[] args) throws InterruptedException{
        Thread.currentThread().setName("线程A");
        Main main = new Main();
        Thread threadB = new Thread(main::writer,"线程B");
        threadB.start();
        //下面的代码让threadB完全运行结束之后才会继续执行线程A的代码,相当于在线程B完成之前,线程A都是阻塞状态
        //这样即使线程A发生了指令的重排序,也不会影响我们最终的输出结果
        threadB.join();
        
        System.out.println("x的值"+main.x);
        System.out.println("Y的值"+main.y);
        System.out.println("flag:"+main.flag);
        System.out.println("线程结束");
    }
    public void writer(){
        System.out.println("开始赋值操作");
        this.x = 100;//这里发生了指令的重排序,先对y赋值,再对x赋值
        this.y = 200;//倘若我们在y赋值之后,在x赋值之前。于主线程输出了四条输出语句,就会导致x=0的情况。
        //因为x=100尚未执行 主线程就已经输出完了
        //在这个例子中,我们通过threadB.join();避免了这一条,让线程B完成重排序后,线程A才能结束阻塞继续执行,从而解决了可见性问题
        this.flag = true;
    }
}