关于JMM中工作内存究竟什么时候同步到主存或者从主存中同步?

发布于 2022-09-12 00:13:53 字数 966 浏览 17 评论 0

//1.完全死循环
new Thread(()->{  
    while(run){  
    }  
    System.out.println("Thead1 end");  
}).start();  
new Thread(()->{  
    try {  
        Thread.sleep(50);  
    } catch (InterruptedException e) {  
        e.printStackTrace();  
    }  
    System.out.println("Thread2 set run");  
  run = false;  
}).start();
//2.休息1ms
new Thread(()->{  
    while(run){  
        try {  
            Thread.sleep(1);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
    System.out.println("Thead1 end");  
}).start();  
new Thread(()->{  
    try {  
        Thread.sleep(50);  
    } catch (InterruptedException e) {  
        e.printStackTrace();  
    }  
    System.out.println("Thread2 set run");  
    run = false;  
}).start();

在1代码中的两个线程能够复现“缺少volatile使得线程1一直循环”的问题。但是加了1ms的sleep之后(2代码),线程1中的循环就能正确马上退出。请求大佬们指点迷津。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

银河中√捞星星 2022-09-19 00:13:54

曾经看到过类似的问题,可以参考下https://segmentfault.com/q/1010000015787014

╰◇生如夏花灿烂 2022-09-19 00:13:54

看过 @蓝星 的回答之后,我改进了一下测试方法,本机cpu为Intel i5-7400,win10,jdk1.8.0_211:
为了避免使用Thread.sleep方法造成困扰,使用循环的方式让cpu运行指定的时间:

public static void sleep(int ms) { 
    //本机测试时循环3,000,000,000L次大概需要1000ms
    for (long i = 0; i < 3000000L * ms; i++) {}  
}
  1. 先读取再设值:

    new Thread(() -> {  
        int loop = 0;  
        while (run) {  
            loop++;  
        }  
        System.out.println("loop=" + loop);  
    }).start();  
    new Thread(()->{  
        sleep(1000);//先暂停1s确保先读取  
        run = false;  
        sleep(2000);  
    }).start();

    测试结果:一直循环

  2. 先设值再读取:

    new Thread(() -> {
        int loop = 0;
        sleep(1000);//先暂停1s确保先设值
        while (run) {
            loop++;
        }
        System.out.println("loop=" + loop);
    }).start();
    new Thread(()->{
        run = false;
        sleep(2000);
    }).start();

    测试结果:总是输出loop=0,说明根本没进循环,并且线程1先退出,过大概2s后线程2退出。

  3. 在测试1的基础上在循环中加入sleep(1):

    new Thread(() -> {
        int loop = 0;
        while (run) {
            loop++;
            sleep(1);//在循环中让cpu执行1ms
        }
        System.out.println("loop=" + loop);
    }).start();
    new Thread(()->{
        sleep(1000);//先暂停1s确保先读取
        run = false;
        sleep(2000);
    }).start();

    测试结果:输出loop=987,loop的次数在950-1050之间,说明在thread2的sleep(1000)之后thread1马上就读取到了最新值,而不是thread2线程结束后。如果sleep(2000),则总是输出loop=1,说明sleep方法结束后立马读取到正确的run值。

    注意: 如果sleep函数里面的循环次数变少,还是不能退出循环。本机测试如果sleep函数中的循环次数小于29000次,则还是会一直循环下去,只要超过30000次,总是能退出循环。

  4. 在测试3的基础上,把sleep(1)替换为实际的for循环:

    new Thread(() -> {
        int loop = 0;
        while (run) {
            loop++;
            //停顿1ms
            for (long i = 0; i < 3000000L * 1; i++) {}
        }
        System.out.println("loop=" + loop);
    }).start();
    new Thread(()->{
        sleep(1000);//先暂停1s确保先读取
        run = false;
        sleep(2000);
    }).start();

    测试结果:一直循环。不管 i 是多少都会一直循环。

  5. 在测试1的基础上加入synchronized语句:

    new Thread(() -> {
        int loop = 0;
        while (run) {
            loop++;
            synchronized (Object.class){}
        }
        System.out.println("loop=" + loop);
    }).start();
    new Thread(()->{
        sleep(1000);//先暂停1s确保先读取
        run = false;
        sleep(2000);
    }).start();

    测试结果:loop=45830307,能结束循环,loop次数没有意义。

  6. 在测试1的基础上加入lock.lock()语句:

    new Thread(() -> {
        int loop = 0;
        while (run) {
            loop++;
            lock.lock(); //static Lock lock = new ReentrantLock();
        }
        System.out.println("loop=" + loop);
    }).start();
    new Thread(()->{
        sleep(1000);//先暂停1s确保先读取
        run = false;
        sleep(2000);
    }).start();

    测试结果:loop=55020323,能正常退出循环。

结论:

  • 线程写能马上同步到主存(测试2)。
  • 循环中执行一个耗时的方法,在这个方法结束后会重新从主存load新的值(测试3)。
  • synchronized关键字和Lock能触发重新从主存load新值操作(测试5,6)。
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文