国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

可見性問題實例

DevTTL / 2462人閱讀

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

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

這個例子很簡單,新建的線程里有一個普通變量stop,用來表示是否結束循環里的自增操作。主線程啟動這個線程后,將該變量置為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秒等待新啟線程執行
        System.out.println("即將置stop值為true");
        v.stopIt();
        Thread.sleep(1000);
        System.out.println("finish main");
        System.out.println("main中通過getStop獲取的stop值:" + v.getStop());
    }
}

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

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

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

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

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

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

    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);//等待新線程執行
        System.out.println("馬上中斷thinkerThread");
        thinkerThread.interrupt();
        System.out.println("已經中斷thinkerThread");
        thinkerThread.join(3000);
        if (thinkerThread.isAlive()) {
            System.err.println("thinkerThread未能在中斷后3s停止");
            System.err.println("JMV bug");
            System.err.println("主線程中檢測thinkerThread的中斷狀態:" + thinkerThread.isInterrupted());
        }
    }
}

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

thinkerThread沒能退出循環,沒看到主線程所置的中斷狀態。

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

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

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

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

擴展閱讀:

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

http://ifeve.com/volatile/

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

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

CSDN上有人發的一個真實案例

via ifeve

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64061.html

相關文章

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

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

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

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

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

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

    jk_v1 評論0 收藏0
  • 高并發 - 基礎

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

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

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

    lauren_liuling 評論0 收藏0

發表評論

0條評論

DevTTL

|高級講師

TA的文章

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