Java併發編程的藝術-初步讀書記錄(持續更新。。。)

併發編程的挑戰
  1. 上下文切換(減小)
    • 無鎖併發編程
    • 避免使用鎖,如將數據的ID按照Hash算法取模分段,不一樣的線程處理不一樣段的數據。(segement)
    • CAS算法:Java的Atomic包使用CAS算法更新數據,不須要加鎖。Compare and swap
    • 使用最少線程:避免建立不須要的線程。
    • 協程:在單線程裏實現多任務的調度,並在單線程裏維持多個任務的切換。(缺實例說明)
  2. 死鎖
    • 避免死鎖
      • 避免同一個線程同時獲取多個鎖。
      • 避免一個線程在鎖內同時佔有多個資源,儘可能保證每一個鎖都只佔用一個資源。
      • 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代內部鎖機制。(待嘗試)
      • 對於數據庫加鎖,加鎖和解鎖必須在一個數據庫鏈接裏,不然會出現鏈接失敗的狀況。
  3. 資源限制
Java併發機制的實現
  1. volatile的應用
    • volatile的定義和實現
      • 定義:Java編程語言容許線程訪問共享變量,爲了確保共享變量能被準確和一致地更新,線程應該確保經過排他鎖單獨得到這個變量。Java提供了volatile,在某些狀況下比鎖要更加方便。若是一個字段被聲明成volatile,Java線程內存模型確保全部線程看到這個變量的值是一致的。
      • 實現原則:
        • Lock前綴指令會引發處理器緩存回寫到內存。
        • 一個處理器的緩存回寫到內存會致使其餘處理器的緩存無效。
  2. synchronized的實現原理及應用
    • synchronized實現同步的基礎:Java中的每個對象均可以當作鎖。具體表現爲:
      • 對於普通同步方法,鎖是當前實例對象。
      • 對於靜態同步方法,鎖是當前類的Class對象。
      • 對於同步方法塊,鎖是Synchronized括號裏配置的對象。
    • Java對象頭
      • synchronized用的鎖是存在Java對象頭裏的。若是對象是數組類型,則虛擬機用3個字寬(Word)存儲對象頭,不然是2個字寬。32位虛擬機中,1字寬等於4字節。
      • (待處理圖片)
    • 鎖的升級與對比
      • JavaSE1.6加入了"偏向鎖"和"輕量級鎖"。鎖4種狀態從低到高爲:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,狀態會隨競爭狀況升級。鎖能夠升級但不能降級,目的是提升得到鎖和釋放鎖的效率。
        • 鎖的優缺點對比:
          •  

  3. 原子操做的實現原理
    • 即爲不可中斷 的一個或者一系列操做。
    •  

  4. Java如何實現原子操做
    • 使用循環CAS實現原子操做
      • CAS實現原子操做的三大問題
        • ABA問題:cas在操做值的時候須要檢查值有沒有發生變化,沒有變化則更新,但若是一值原理爲A,變成B後又變成A,使用CAS檢查時發現值沒有變化,但實際上卻發生了變化。ABA問題的解決思路就是增長版本號,在變量前面加上版本號,每次變化則加1,那麼A-B-A則變爲1A-2B-3A。Java1.5開始,JDK的Atomic包裏提供了AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法的做用是首先檢查當前引用是否等於預期引用,而且檢查當前標誌是否等於預期標誌,所有相等則以原子方式將改引用和該標記的值設置爲給定的更新值。
        • 循環時間長開銷大:自旋CAS長時間不成功,會給CPU帶來很是大的執行開銷。(優化:使用JVM支持處理器的pause命令)
        • 只能保證一個共享變量的原子操做:當對一個共享變量操做時,可使用循環CAS的方式保證原子操做,但對於多個共享變量時,沒法保證原子性,這時用鎖。 其餘辦法:將多個共享變量合併成一個用CAS操做。1.5後用AtomicReference來操做保證。
    • 使用鎖機制來實現原子操做
Java內存模型
  1. Java內存模型的基礎
    • 併發編程模型的兩個關鍵問題
      • 線程之間如何通訊
        • 命令式編程中,通訊方式有兩種:共享內存和消息傳遞。在共享內存的併發模型裏,線程之間共享程序的公共狀態,經過寫-讀內存中的公共狀態進行隱式通訊。在消息傳遞的的併發模型裏,線程之間沒有公共狀態,線程之間經過消息傳遞來顯示進行通訊。
      • 線程以前如何同步
        • 同步是指程序中用於控制不一樣線程間操做發生相對順序的機制。在共享內存的併發模型中,同步是顯示進行的。
      • Java採用的是共享內存模型,通訊是隱式進行的。
    • Java內存模型的抽象結構
      • 在Java中,全部實例域、靜態域和數組元素都存儲在堆內存中,堆內存在線程之間共享局部變量,方法定義參數和異常處理器參數不會在線程之間共享,它們不會有內存可見性問題,也不受內存模型的影響。
      • Java線程之間的通訊由Java內存模型(JMM)控制,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。從抽象的角度來看,JMM定義了線程和主內存之間的抽象關係:線程之間的共享變量存儲在主內存中,每一個線程都有一個私有的本地內存,本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存、寫緩衝區、寄存器以及其餘的硬件和編譯器優化。Java內存模型的抽象示意如圖3-1所示:

         

      • 從圖3-1看到,若是A和B線程之間須要通訊的時候,必須經歷下面2個步驟
        • 一、線程A把本地內存A中更新過的共享變量刷新到主內存中去。
        • 二、線程B到主內存中讀取線程A以前已更新過的共享變量。
        • 過程如圖所示

           

        • 總結:JMM經過控制主內存與每一個線程的本地內存之間的交互,來爲Java提供內存可見性保證。
    • 從源代碼到指令序列的重排序
      • 在執行程序時,爲了提升性能,編譯器和處理器經常會對指令進行重排序,分爲如下3種
        • 編譯器優化的重排序:編譯器在不改變單線程程序語義的前提下,能夠從新安排語句的執行順序。
        • 指令級並行的重排序: 現代處理器採用了指令級並行技術來將多條指令重疊執行。若是不存在數據依賴性,處理器能夠改變語句對應機器指令的執行順序。
  • 內存系統的重排序: 因爲處理器使用緩存和讀/寫緩衝區,這使得加載和存儲操做看上去多是在亂序執行。
相關文章
相關標籤/搜索