1. 程式人生 > >Java併發基礎—執行緒安全性

Java併發基礎—執行緒安全性

執行緒安全性

        當多個執行緒訪問某個類時,不管執行時環境採用何種排程方式或者這些程序如何交替執行,並且在主調程式碼中無需任何額外的同步或協同,這個類都能表現出正確的行為,那麼就稱這個類是執行緒安全的。

執行緒安全性——原子性

        提供了互斥訪問,同一時刻只能有一個執行緒來對它進行操作。

        原子性——Atomic包

        CAS原理:Unsafe類提供了一個包含do while語句的方法,該方法的迴圈條件是compareAndSwapXxx方法,該方法的意思是拿當前物件的值 (工作記憶體)跟底層的值(主記憶體)進行比較,如果相同則執行對應的操作,如果不相同,則不停地執行迴圈體中的方法來取最新的值。

        AtomicStampReference:核心是為了解決CAS的ABA問題

        什麼是ABA問題?當進行CAS的操作時,其它執行緒將變數的值A改為B,但是又改回為A,等當前執行緒用期望值與A進行比較的時候,發現A變數沒有變,於是CAS就將A值進行了交換操作,這個時候實際上A變數的值已經被其它執行緒改變過,這與設計思想是不符合的。

        那麼,ABA問題的解決思路是,每次變數更新的時候,把變數的版本號加1,這樣只要變數被某個執行緒修改過,該變數的版本號就會發生遞增變化。

        原子性——鎖

        synchronized修飾的四種物件:

        1、修飾程式碼塊,大括號括起來的程式碼,作用於呼叫的物件

        2、修飾方法,整個方法,作用於呼叫的物件

        3、修飾靜態方法,整個靜態方法,作用於所有物件

        4、修飾類,括號括起來的部分,作用於所有物件

        原子性——對比

        synchronized:不可中斷鎖,適合競爭不激烈,可讀性好

        Lock:可中斷鎖,多樣化同步,競爭激烈時能維持常態

        Atomic:競爭激烈時能維持常態,比Lock效能好;只能同步一個值

執行緒安全性——可見性

        一個執行緒對主記憶體的修改可以及時地被其它執行緒觀察到。

        導致共享變數線上程間不可見的原因:

        1、執行緒交叉執行

        2、重排序結合線程交叉執行

        3、共享變數更新後的值沒有在工作記憶體與主存間及時更新

        JMM(Java記憶體模型)關於synchronized的兩條規定:

        1、執行緒解鎖前,必須把共享變數的最新值重新整理到主記憶體

        2、執行緒加鎖時,將清空工作記憶體中共享變數的值,從而使用共享變數時需要從主記憶體中重新讀取最新的值(注意,加鎖與解鎖是同一把鎖)

        可見性——volatile

        通過加入記憶體屏障和禁止重排序優化來實現。

        1、對volatile變數寫操作時,會在寫操作後加入一條store屏障指令,將本地記憶體中的共享變數值重新整理到主記憶體

        2、對volatile變數讀操作時,會在讀操作前加入一條load屏障指令,從主記憶體中讀取共享變數

執行緒安全性——有序性

        一個執行緒觀察其它執行緒中的指令執行順序,由於指令重排序的存在,該觀察結果一般雜亂無序。

        Java記憶體模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單執行緒程式的執行,卻會影響到多執行緒併發執行的正確性。

        有序性——happens-before原則

1、程式次序規則:一個執行緒內,按照程式碼順序,書寫在前面的操作先行發生於書寫在後面的操作

        2、鎖定規則:一個unlock操作先行發生於後面對同一個鎖的lock操作

        3、volatile變數規則:對一個變數的寫操作先行發生於後面對這個變數的讀操作

        4、傳遞規則:如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出操作A先行發生於操作C

        5、執行緒啟動規則:Thread物件的start()方法先行發生於此執行緒的每一個動作

        6、執行緒中斷規則:對執行緒interrupt()方法的呼叫先行發生於被中斷執行緒的程式碼檢測到中斷事件的發生

        7、執行緒終結規則:執行緒中所有的操作都先行發生於執行緒的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive() 的返回值手段檢測到執行緒已經終止執行

        8、物件終結規則:一個物件的初始化完成先行發生於它的finalize()方法的開始

        總結:如果兩個操作的執行順序不能從happens-before原則中推匯出來,那麼這就無法保證它們的有序性,虛擬機器可以隨意地對它們進行重排序。