成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

可見性問題實例

DevTTL / 2475人閱讀

摘要:主線程啟動這個線程后,將該變量置為,觀察線程是否打印出那行,如果存在可見性問題,主線程修改值為,線程看的值應該還是。

說到并發(fā)安全時,我們常提及可見性的問題,通俗點講就是線程1看不到線程2寫入變量v的值(更專業(yè)的解釋以及是什么導致可見性問題,又該如何解決,見擴展閱讀),但一直偏于理論,實際中有沒有因可見性而導致問題的例子呢?回答是肯定的,接下來我們一起來看幾個例子。

這個例子很簡單,新建的線程里有一個普通變量stop,用來表示是否結束循環(huán)里的自增操作。主線程啟動這個線程后,將該變量置為true,觀察線程是否打印出finish loop那行,如果存在可見性問題,主線程修改stop值為true,線程v看stop的值應該還是false。

class VisibilityThread extends Thread {
    private boolean stop;

    public void run() {
        int i = 0;
        System.out.println("start loop.");
        while(!getStop()) {
            i++;
        }
        System.out.println("finish loop,i=" + i);
    }

    public void stopIt() {
        stop = true;
    }

    public boolean getStop(){
        return stop;
    }
}

public class VisibilityTest {
    public static void main(String[] args) throws Exception {
        VisibilityThread v = new VisibilityThread();
        v.start();

        Thread.sleep(1000);//停頓1秒等待新啟線程執(zhí)行
        System.out.println("即將置stop值為true");
        v.stopIt();
        Thread.sleep(1000);
        System.out.println("finish main");
        System.out.println("main中通過getStop獲取的stop值:" + v.getStop());
    }
}

我們先來執(zhí)行一遍(操作系統(tǒng):XP,下同。JDK:見圖示):

執(zhí)行結果如上圖,有人該問了,線程v最終停下來了,這不是表示它看到stop值為true了嗎?是的,確實如此。但讓我們再看一個這個程序的執(zhí)行結果。

這一次,我們發(fā)現(xiàn)程序一直未能結束,表示線程v看到stop的值是false,但是主線程打印出的值卻是true。

對比兩次的執(zhí)行方式,我們發(fā)現(xiàn)后一次加上了-server選項。顯示version的時候也由Client VM變成了Server VM。那么Client VM與Server VM有什么區(qū)別在哪里?簡單地講,Client VM啟動時做了一般優(yōu)化,耗時少,啟動快,但程序執(zhí)行的也相對也較慢;Server VM啟動的時候做了更多優(yōu)化,耗時多,啟動慢,但程序執(zhí)行快。如果在運行java命令的時候沒有指定具體模式的時候,會有一個默認值,這個默認值隨硬件和操作系統(tǒng)的不同而不同,這里有張JDK 1.6在各平臺默認VM模式的圖。

我們再來看個例子,這個例子源于hotspot VM的一個bug:

public class InterruptedVisibilityTest {
    public void think() {
        System.out.println("新線程正在執(zhí)行");
        while (true) {
            if (checkInterruptedStatus()) break;
        }
        System.out.println("新線程退出循環(huán)");
    }

    private boolean checkInterruptedStatus() {
        return Thread.currentThread().isInterrupted();
    }

    public static void main(String[] args) throws Exception {
        final InterruptedVisibilityTest test = new InterruptedVisibilityTest();
        Thread thinkerThread = new Thread("Thinker") {
            public void run() {
                test.think();
            }
        };
        thinkerThread.start();
        Thread.sleep(1000);//等待新線程執(zhí)行
        System.out.println("馬上中斷thinkerThread");
        thinkerThread.interrupt();
        System.out.println("已經(jīng)中斷thinkerThread");
        thinkerThread.join(3000);
        if (thinkerThread.isAlive()) {
            System.err.println("thinkerThread未能在中斷后3s停止");
            System.err.println("JMV bug");
            System.err.println("主線程中檢測thinkerThread的中斷狀態(tài):" + thinkerThread.isInterrupted());
        }
    }
}

這個例子也很簡單,thinkerThread一直檢查中斷狀態(tài),主線程在啟動thinkerThread之后的某個時刻調(diào)用interrupt中斷thinkerThread。在《The Java Language Specification Java SE 7 Edition》§17.4.4中我們能夠看到,如果線程1調(diào)用線程2的interrupt方法,那么所有線程(包括線程2)都能通過Thread.isInterrupted方法來檢測到這個中斷狀態(tài)。這里直接用hotspot VM的-server模式執(zhí)行一下,結果如下圖:

thinkerThread沒能退出循環(huán),沒看到主線程所置的中斷狀態(tài)。

后面這個例子是hotpost VM的一個bug導致的,在最新的hotspot中應該已經(jīng)被修復了(筆者未測試最新版)。其它VM如IBM J9,JRockit,harmony等并沒有發(fā)現(xiàn)這樣的bug。說這是bug,是因為JLS中規(guī)定了main發(fā)出的中斷必須對thinkerThread可見。但是,如第一個例子,則不是bug,因為JLS是允許這種行為的。當在第一個例子的循環(huán)中的i++后面加上一句Thread.yield()調(diào)用(該調(diào)用在規(guī)范中并沒有特殊內(nèi)存語義),這我使用的這個版本的VM上,就看不到可見性問題了。這也說明,JVM的優(yōu)化是無法預知的,允許可見性的地方不一定就真會出現(xiàn)或一直出現(xiàn)。

JLS允許未充分同步的代碼出現(xiàn)可見性問題,但是某個實際的JVM完全可以實現(xiàn)的比JLS上規(guī)定的更強,比如不允許可見性問題出現(xiàn),那么,在這樣的JVM上就展現(xiàn)不出這樣的問題了。第一個例子這里只是運行在hotpost下,也許在其它JVM下同樣采用最優(yōu)化的方式執(zhí)行,可能并不會出現(xiàn)這里的問題。

在我們編碼的時候,也許并不知道代碼會跑在什么樣的系統(tǒng)上,不知道會采用什么樣的JVM,為了使得寫出的代碼更健壯,我們只能按照規(guī)范所規(guī)定的最低保證去編碼,要避免這類問題,只有保證代碼充分同步,避免數(shù)據(jù)爭用,而不應該依賴于某個具體JVM實現(xiàn)。即使是具體的某款JVM,不同的版本間也可能存在著差異。

最后,這樣的例子啟發(fā)我們,測試代碼的時候應盡可能啟用各JVM的最佳優(yōu)化模式。

擴展閱讀:

至此,我們已經(jīng)了解到實際中多線程運行真的會出現(xiàn)這樣的場景。為什么會出現(xiàn)可見性問題?有什么解決方案?下面鏈接中的內(nèi)容為我們提供了專業(yè)的解答。

http://ifeve.com/volatile/

http://ifeve.com/java-memory-model-0/

http://ifeve.com/java-concurrency-constructs/

CSDN上有人發(fā)的一個真實案例

via ifeve

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64061.html

相關文章

  • 貓頭鷹的深夜翻譯:Volatile的原子, 可見和有序

    摘要:有可能一個線程中的動作相對于另一個線程出現(xiàn)亂序。當實際輸出取決于線程交錯的結果時,這種情況被稱為競爭條件。這里的問題在于代碼塊不是原子性的,而且實例的變化對別的線程不可見。這種不能同時在多個線程上執(zhí)行的部分被稱為關鍵部分。 為什么要額外寫一篇文章來研究volatile呢?是因為這可能是并發(fā)中最令人困惑以及最被誤解的結構。我看過不少解釋volatile的博客,但是大多數(shù)要么不完整,要么難...

    Lionad-Morotar 評論0 收藏0
  • java 關鍵字總結

    摘要:關鍵字總結有個關鍵字,它們是接下來對其中常用的幾個關鍵字進行概括。而通過關鍵字,并不能解決非原子操作的線程安全性。為了在一個特定對象的一個域上關閉,可以在這個域前加上關鍵字。是語言的關鍵字,用來表示一個域不是該對象串行化的一部分。 java 關鍵字總結 Java有50個關鍵字,它們是: abstract do implements private ...

    honmaple 評論0 收藏0
  • 多線程學習筆記(1):volatile和synchronized

    摘要:今天開始整理學習多線程的知識,談談最重要的兩個關鍵字和。但是這樣一個過程比較慢,在使用多線程的時候就會出現(xiàn)問題。有序性有序性是指多線程執(zhí)行結果的正確性。這種機制在多線程中會出現(xiàn)問題,因此可以通過來禁止重排。 今天開始整理學習多線程的知識,談談最重要的兩個關鍵字:volatile和synchronized。 一、三個特性 1、原子性 所謂原子性操作就是指這些操作是不可中斷的,要么執(zhí)行過程...

    jk_v1 評論0 收藏0
  • 高并發(fā) - 基礎

    摘要:異步非阻塞方式,任務的完成的通知由其他線程發(fā)出。并發(fā)并行死鎖饑餓活鎖死鎖線程持有,線程持有。如等,在多線程情況下,該操作不是原子級別的而是原子的,所以一般用于狀態(tài)標記。 同步/異步、阻塞/非阻塞 同步/異步是 API 被調(diào)用者的通知方式。阻塞/非阻塞則是 API 調(diào)用者的等待方式(線程掛機/不掛起)。 同步非阻塞 Future方式,任務的完成要主線程自己判斷。如NIO,后臺有多個任務在...

    phpmatt 評論0 收藏0
  • Java進階3 —— 類和接口設計原則

    摘要:首當其沖的便是接口中的每個聲明必須是即便不指定也是,并且不能設置為非,詳細規(guī)則可參考可見性部分介紹。函數(shù)式接口有著不同的場景,并被認為是對編程語言的一種強大的擴展。抽象類與中的接口有些類似,與中支持默認方法的接口更為相像。 原文鏈接:http://www.javacodegeeks.com/2015/09/how-to-design-classes-and-interfaces.htm...

    lauren_liuling 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<