鍍金池/ 問答/HTML5  Java/ 《Java并發(fā)編程實(shí)戰(zhàn)》一段代碼的疑惑

《Java并發(fā)編程實(shí)戰(zhàn)》一段代碼的疑惑

《Java并發(fā)編程實(shí)戰(zhàn)》3.1節(jié)的一段代碼:

public class NoVisibility {

    private static boolean ready;
    private static int number;
    
    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }
    
    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}

按照書中所講,這段代碼不一定輸出42,有可能陷入死循環(huán)。原因是代碼中沒有足夠的同步機(jī)制,無法保證主線程寫入的ready他number對(duì)讀線程是可見的。也有可能是0,原因是“重排序”,讀線程看到了主線程寫入的ready卻沒有看到number。但是經(jīng)過多次測(cè)試,始終輸出42,既沒有輸出0,也沒有死循環(huán)。為了驗(yàn)證書中所說的主線程修改對(duì)讀線程不可見,我改寫了代碼:

public class NoVisibilityLoop {

    private static int number;
    
    private static class ReaderThread extends Thread {
        public void run() {
            while (true) {
                System.out.println(number);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        new ReaderThread().start();
        Thread.sleep(1000);
        number = 42;
    }
}

多次測(cè)試輸出結(jié)果是:
0
0
0
0
0
42
42
42
42
這個(gè)結(jié)果說明了主線程寫入的number對(duì)讀線程是可見的。但是這本書的作者一堆大牛,不會(huì)搞錯(cuò)的。還請(qǐng)大家?guī)兔纯次业尿?yàn)證方法哪里出問題了,謝謝。
運(yùn)行環(huán)境:jdk 1.8.0_101,server模式。

回答
編輯回答
懶洋洋

http://www.cnblogs.com/dolphi...
建議看下這篇文章 講的很詳細(xì) 看完相信你能理解

2017年12月9日 03:29
編輯回答
情皺

原文的意思是讓你重復(fù)多次運(yùn)行這段代碼
大概運(yùn)行幾十次甚至上百次,可能出現(xiàn)一次死循環(huán),而這個(gè)在實(shí)際開發(fā)中是不可取的

而這是代碼設(shè)計(jì)缺陷的問題,所以建議不要這么編程,要保持主副線程的同步性不要用這樣的寫法,你繼續(xù)往后看,這書既然這么說,可能會(huì)有別的解決方案來幫你理解他描述的原理

2017年2月16日 20:31
編輯回答
落殤

應(yīng)該是JDK版本差異造成的

2017年1月6日 11:08
編輯回答
貓館

首先測(cè)試多次正確也不能保證完全就是正確的,其次這本書是jdk5和6了,jdk8估計(jì)做了些優(yōu)化,

private static boolean ready;
private static int number;

private static class ReaderThread extends Thread {
    @Override
    public void run() {
        int i=0;
        while (!ready) {
            i++;
//                Thread.yield();
        }
        System.out.println(number);
    }
}

public static void main(String[] args) throws Exception {
    new ReaderThread().start();
    TimeUnit.MILLISECONDS .sleep(9);
    number = 42;
    ready = true;
}

把 thread.yield 換成 i++ 或者 sleep 與否 結(jié)果都有差別
這種執(zhí)行順序是指令重排決定的,不用volatile沒法保證

2018年1月13日 00:50