细读jsr中的yield语义,或者不是我们想象中的那样

这只是我的个人观点,如有错误,希望你可以指出。

 

首先英文原版

 

中文译版

 

 

 我觉得“不需要”还会让人产生误解,应该译为不一定要。

 

很多时候会被断章取义地理解,我们一定要有“不一定”,“可能”的意识,下面给出一段代码演示,亲测。

    static boolean close=false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{ //且称线程1
            int count=1;
            while(!close){
                Thread.yield(); //sleep()有同样的效果
                count++;
            }
            System.out.println("yield"+count);//等待io
        }).start();

        Thread.sleep(100);
        close=true;
    }

//yield77616,这个数在70000-110000左右

线程是会停止的,这时候断章取义者就会百思不得其解。

 

那么jsr中的又是什么意思呢?

是基于这样的一个假设的:

主线程在cpu0上运行。

线程1一直在cpu1上运行,虽然线程1 yield,但是没有别的线程来使用cpu1,意思为cpu1中寄存器,缓存中存着的还是线程1的数据,如变量close。

然而,如果在线程1 yield或者sleep的时候,操作系统调度别的线程来使用cpu1,那么cpu1中存放的旧数据将会刷回主存(实际刷回时机视实际情况,不同的cpu应该会有不同的策略),然后存放别的线程所需的数据。那么等到线程1 yield或者sleep完毕,到它使用cpu1(不一定是cpu1)的时候,发现没有命中缓存,就会从主存中把变量close读取进来,而这个时候主存当中的close已经被主线程修改为true了,因此循环结束。

而实际情况是,时时刻刻都有大量的线程在工作,你放弃了就会有别人来用。

所以它真正强调的意思是“不主动”,而像volatile就会主动去做某些措施。

 

继续补充

static boolean close=false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            //int count=1;
            while(!close){
                System.out.println("io操作");
            }
            System.out.println("io操作完毕");
        }).start();

        Thread.sleep(100);
        close=true;
    }
//    io操作
//    io操作
//      .
//      .
//      .
//    io操作
//    io操作完毕

如上,线程1在进行IO操作的时候,将会有别的线程来使用cpu1,线程1照样结束循环。

只有这样才不会结束

while(!close){
            }

 

总结

 一言以蔽之,只要cpu没有命中缓存,就会从主存中读取。

 

再补充

可能会觉得,那这样的话,yield和sleep就像是普通的正常的操作(反正肯定会有别的线程来使用cpu)啊,有必要这样强调吗?

 我觉得

一是文档的规范性,完整性。

二,也是曾在“为什么java的称为虚拟机,而python的称为解释器”这样的问题中,看到的一个答案。(不保证正确)

jvm是模仿着硬件去设计的,像一些操作,取操作码,取操作数,push,store等指令。如果按照jvm去配置硬件,有可能做到cpu中就只有jvm的线程在跑,或者说通俗点说jvm就是操作系统。那这样的话,就真的满足之前的假设了——“线程1一直在cpu1上运行,虽然线程1 yield,但是没有别的线程来使用cpu1”。

 

posted @ 2020-02-26 12:02  Allen没有青春  阅读(189)  评论(0编辑  收藏  举报