《深刻理解Java虛擬機》筆記--第十三章、線程安全與鎖優化

先保證併發的正確性,而後在此基礎上來實現高效。
線程安全:
    當多個線程訪問一個對象時,若是不考慮這些線程在運行時環境下的調度和交替執行,也不須要進行額外的同步,或者在調用方進行任何其餘的協調操做,調用這個對象的行爲均可以得到正確的結果,那這個對象就是線程安全的。——Brian Goetz
    線程安全是限定於多個線程之間存在共享數據訪問這個前提下的。
    線程安全由強至弱的「安全程度」:不可變、絕對線程安全、相對線程安全、線程兼容和線程對立。
        不可變:
            不可變的對象必定是線程安全的,只要一個不可變的對象被正確的構建出來,那其外部的可見狀態永遠也不會改變,永遠也不會看到它在多個線程之中處於不一致的狀態。
            若是一個共享數據是基本數據類型,那麼只要在定義的時候使用final關鍵字修飾它就能夠保證不可變;若是是一個對象,那就須要保證對象的行爲不會對其狀態產生任何影響。
        絕對線程安全:
            徹底知足Brian Goetz給出的線程安全定義,在Java API中標註本身是線程安全的類,大多數都不是絕對的線程安全。
        相對線程安全:
            一般意義上的線程安全,保證對這個對象單獨的操做是線程安全的。
        線程兼容:
            對象自己並非線程安全的,可是能夠經過在調用端正確的使用同步手段來保證對象在併發環境中安全的使用,咱們日常說一個類不是線程安全的,絕大多數指的都是這種狀況。
        線程對立:
            無論調用端是否採起了同步措施,都沒法在多線程環境中併發使用的代碼。
線程安全的實現方式:
    主要介紹虛擬機如何實現同步與鎖。
    互斥同步(Mutual Exclusion & Synchronization):
        也被稱爲阻塞同步(Blocking Synchronization),屬於悲觀的併發策略,總認爲只要不去作正確的同步措施,那就確定會出現問題,不管共享數據是否真的會出現競爭,都要進行加鎖、用戶態核心態轉換、維護鎖計數器和檢查是否有被阻塞的線程須要被喚醒等操做。
        同步是指多個線程併發訪問共享數據時,保證共享數據在同一時刻只被一條線程使用。互斥是實現同步的一種手段,臨界區、互斥量和信號量都是主要的互斥實現方式。互斥是方法,同步是目的。
        在Java裏最基本的互斥手段就是synchronized關鍵字,synchronized關鍵字在編譯後,會在同步塊的先後分別造成monitorenter和monitorexit這兩個字節碼指令。
        還可使用java.util.concurrent包中的重入鎖(ReentrantLock)來實現同步,ReentrantLock比synchronized增長了一些高級功能:等待可中斷、可實現公平鎖以及鎖能夠綁定多個條件。
    非阻塞同步(Non-Blocking Synchronization):
         基於衝突檢測的樂觀併發策略。通俗的講就是先進行操做,若是沒有其餘線程爭用共享數據,那操做就成功了;若是共享數據有爭用,產生了衝突,那就再進行其餘的補償措施(如重試),這些補償操做的實現一般不須要將線程掛起,故稱非阻塞同步。
        非阻塞同步須要硬件指令集的發展才能實現,從硬件上保證一個從語義上看起來須要屢次操做的行爲只經過一條處理器指令就能完成:測試並設置、獲取並增長、交換、比較並交換、加載連接/條件存儲。
    無同步方案:
        有一些代碼天生就是線程安全的,不須要同步。
        可重入代碼(Reentrant Code):純代碼,具備不依賴存儲在堆上的數據和公用的系統資源,用到的狀態量都由參數中傳入,不調用非可重入的方法等特徵,它的返回結果是能夠預測的。
        線程本地存儲(Thread Local Storage):把共享數據的可見範圍限制在同一個線程以內,這樣就無須同步也能保證線程之間不出現數據爭用問題。能夠經過java.lang.ThreadLocal類來實現線程本地存儲的功能。
鎖優化:
    爲了在線程之間更高效的共享數據,以及解決競爭問題,從而提升程序的執行效率,建立了各類鎖優化技術:適應性自旋(Adaptive Spinning)、鎖消除(Lock Elimination)、鎖粗化(Lock Coarsening)、輕量級鎖(Lightweight Locking)、偏向鎖(Biased Locking)等。
    自旋鎖與自適應自旋:
        線程掛起和恢復的操做都須要轉入內核態中完成,這些操做給系統的併發性能帶來了很大的壓力,在許多應用中,共享數據的鎖定狀態只會持續很短的一段時間,爲了這段時間去掛起和恢復線程並不值得,可讓後請求鎖的線程等待一下子,但不放棄處理器的執行時間,讓線程執行一個忙循環(自旋)。
        自旋鎖默認的自旋次數值是10次,可使用參數-XX:PreBlockSpin更改。
        自適應自旋意味着自旋的時間再也不固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。
    鎖消除:
        虛擬機即時編譯器在運行時,對一些代碼上要求同步,可是被檢測到不可能存在共享數據競爭的鎖進行消除。鎖消除的主要斷定依據來源於逃逸分析的數據支持。
    鎖粗化:
        若是虛擬機探測到有一系列連續操做都對同一個對象反覆加鎖和解鎖,將會把加鎖同步的範圍擴展(粗化)到整個操做序列的外部。
    輕量級鎖:
        使用對象頭的Mark Word中鎖標誌位代替操做系統互斥量實現的鎖。
        輕量級鎖並非用來代替重量級鎖,它的本意是在沒有多線程競爭的前提下,減小傳統的重量級鎖使用操做系統互斥量產生的性能消耗。
        輕量級鎖是在無競爭的狀況下使用CAS(Compare-and-Swap)操做去消除同步使用的互斥量。
    偏向鎖:
        和輕量級鎖原理基本一致,但偏向鎖在無競爭的狀況下把整個同步都消除掉,連CAS操做都不作了。
相關文章
相關標籤/搜索