Thread.sleep() 对线程可见性的影响

问题描述

这几天在深入了解多线程,当学习到关于线程可见性的研究时,我写 demo 突然发现一个问题:
下面的代码是模拟线程可见性的。主线程(main)先启动,然后启动子线程(ThreadVolatileDemo ),flag 初始值为 true,然后主线程将 flag 设置为 false。
由于线程的可见性,主线程的 flag = false 会存入主内存,然后子线程去读取主内存,while 循环结束。
可是!!!!情况有点不对!!!

public class ThreadVolatile {
    public static void main(String[] args) {
        ThreadVolatileDemo demo = new ThreadVolatileDemo();
        demo.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        demo.setFlag(false);
        System.out.println("flag == false");
    }
}

class ThreadVolatileDemo extends Thread {
    public boolean flag = true;
    //public volatile boolean flag = true;
    @Override
    public void run() {
        System.out.println("子线程开始执行");
        while (flag) {
            //System.out.println("while循环中");
        }
        System.out.println("子线程执行结束");
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

Thread.sleep() 对线程可见性的影响_第1张图片
执行结果会卡住,“子线程执行结束”这句话不会被打印出来!

我们下面修改一点代码,把主线程的休眠注释掉!

public class ThreadVolatile {
    public static void main(String[] args) {
        ThreadVolatileDemo demo = new ThreadVolatileDemo();
        demo.start();
        //try {
        //    Thread.sleep(3000);
        //} catch (InterruptedException e) {
        //    e.printStackTrace();
        //}
        demo.setFlag(false);
        System.out.println("flag == false");
    }
}

class ThreadVolatileDemo extends Thread {
    public  boolean flag = true;
    //public volatile boolean flag = true;

    @Override
    public void run() {
        System.out.println("子线程开始执行");
        while (flag) {
            //System.out.println("while循环中");
        }
        System.out.println("子线程执行结束");
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

此时的执行结果是什么?看下图:
Thread.sleep() 对线程可见性的影响_第2张图片
子线程执行结束这句话竟然打印出来了?
这代表子线程读取到了主内存中的 flag == false
问题很严重!!!
是因为线程休眠后子线程就不会去读主内存?
还是因为线程休眠后主线程不会写入主内存?

解题

其实 Thread.sleep() 不会影响线程可见性啦!
出现上面问题的原因是,Thread.sleep() 影响了线程的调度

第一种情况

第一种情况设置了线程休眠,此时主线程休眠,此时主内存和子线程栈中的 flag = true,而子线程启动后,直接进入了 while 循环,当子线程在 while 循环的时候,主线程执行了 flag = false,因为没有使用 volatile 关键字修饰变量 flag,所以 flag 的变化不是线程可见的,因此子线程的循环仍然使用自己栈中的 flag = true。
这样的话,输出结果卡住的原因就找到了!

第二种情况

第二种情况,由于 CPU 的执行线程的特性,没有线程休眠时,很可能在 while 循环前主线程就把 flag = false 写到了主内存中,当子线程执行 while 循环的时候,子线程的栈中 flag 的值已经被刷新成了 false,因此不会卡住。

思考

我代码中还注释了一行,就是在 while 循环中打印东西,如果这一句不注释,会是什么结果呢?

结果我就不放出来了,各位可以试一试,直接上答案:

除了 volatile 关键字会刷新副本(刷新子线程中值与主内存一样),其他同步方法执行的时候也会刷新副本,比如 println。

当你在 while 循环中打印的时候,副本已经被刷新了,此时,就算加上线程休眠,结果也不会被卡住。

学无止境,上述为个人前线理解,如有不对,请各位批评指正!
END

你可能感兴趣的:(Java,java,多线程,thread,volatile,后端)