关于System.out.println和Thread.sleep对多线程可见性的影响

文章通过一个示例代码解释了Java中线程可见性的问题,当一个线程修改了共享变量,其他线程可能无法立即感知。这通常发生在线程从主内存复制变量到工作内存,修改后再刷新回主内存的过程中。示例中,未使用volatile修饰的stop变量导致其他线程无法检测到其变化,从而造成死循环。使用volatile关键字或synchronized同步机制可以确保可见性。
摘要由CSDN通过智能技术生成

可见性

可见性是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。

我们知道共享变量是存储在主内存中的,每个线程使用的时候从主内存复制到自己的工作内存,所以线程在操作这个变量的时候是在自己的工作内存中,操作完毕才会将值刷新到主内存,也就是说其他线程刷新了主内存的值,而我们当前线程是无法感知的,会继续操作自己工作空间的值,进而最终导致主内存的共享变量不是我们预期的结果。

public class VolatileTest {
    /**
     * 执行出现死循环,加上volatile后则正常
     */
    static boolean stop = false;
//	volatile boolean stop = false;

    public static void main(String[] args) throws Exception{
        VolatileTest v = new VolatileTest();
        Thread ta = new Thread(()->v.execute());
        ta.start();
        Thread ta1 = new Thread(()->v.execute2());
        ta1.start();
        Thread ta2 = new Thread(()->v.execute3());
        ta2.start();
        Thread.sleep(2000);
        Thread tb = new Thread(()->v.shutdown());
        tb.start();
        Thread.sleep(500);
        System.out.println("stop="+String.valueOf(stop));
    }

    public void execute(){
        while(!stop){
            String a = "a";
//            stop = false;
//            System.out.println("123"); // 1)当有synchronized()同步机制的时候,会保证可见性。
                                         // 2)jvm会尽可能的在空闲的时候去同步主存的共享变量,所以当线程存在Thread.sleep时候,会尽可能同步数据
        }
        System.out.println("execute:stop");
    }
    public void shutdown(){
        stop = true;
        System.out.println("do stop");
    }


    public void execute2(){
        System.out.println("execute2 start");
        while(!stop){
            String a = "a";
            try {
                Thread.sleep(500);
            } catch (InterruptedException interruptedException) {
                interruptedException.printStackTrace();
            }
        }
        System.out.println("execute2:stop");
    }

    public void execute3(){
        System.out.println("execute3 start");
        while(!stop){
            String a = "a";
            System.out.println("123");
        }
        System.out.println("execute3:stop");
    }


}

执行完结果:

do stop
…(程序一直在运行,进入无线循环)

我们看到do stop这个线程已经将stop修改为false,但是线程还没没终止,这就印证了我们的说法。

过程:

  1. main线程,将将主存中flag的值复制到自己的工作内存中。
  2. 接着启动线程execute,此时该线程也会将主存中stop的值复制到该线程的工作内存中。
  3. 接着while循环从自己工作内存中读取stop的值,一直为true,一直循环。
  4. 2s后启动线程shutdown将stop的值从自己的工作内存中修改为false。
  5. 虽然线程shutdown工作内存flag的值被修改了,但是什么时候刷新到主内存是不确定的。
  6. 即使立即刷新到主内存,但是其他线程也是无法感知的。
  7. 所以while循环一直读取的自己工作内存的flag,就处于无限循环中。
  8. 线程execute2和线程execute3感知到了,说明synchronized和Thread.sleep(ms, ns)可以保证可见性。
  9. 将stop 添加volatile后,execute1也可以感知到stop 值的变化。

结论:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值