深刻JVM--高效併發(讀書筆記)

Java內存模型與線程

  • Java內存模型java

    • Java內存模型的主要目的是定義程序中各類變量的訪問規則,即關注在虛擬機中把變量值存儲到內存和從內存中取出變量值這樣的底層細節。此處的變量(Variables)與Java編程中所說的變量有所區別,它包括了實例字段、靜態字段和構成數組對象的元素,可是不包括局部變量與方法參數,由於後者是線程私有的
      • 全部的變量都存儲在主內存中,每條線程有本身的工做內存(相似緩存)
    • 線程的工做內存中保存了被該線程使用的變量的主內存副本,線程對變量的全部操做(讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的數據
      • 主內存大體對應Java堆中的對象實例數據部分,工做內存基本對應虛擬機棧中的部分區域
    • 工做內存與主內存之間的交互
      • 不容許一個變量從主內存讀取了但工做內存不接受,或者工做內存發起回寫了但主內存不接受的狀況出現
      • 變量在工做內存中改變了以後必須把該變化同步回主內存。
      • 不容許一個線程無緣由地把數據從線程的工做內存同步回主內存中。
      • 一個新的變量只能在主內存中「誕生」
      • 一個變量在同一個時刻只容許一條線程對其進行lock操做,但lock操做能夠被同一條線程重複執行屢次,屢次執行lock後,只有執行相同次數的unlock操做,變量纔會被解鎖(可重入)
      • 若是對一個變量執行lock操做,會清空原來這個變量在工做內存的值,也就是要求從新從主內存read、load(lock操做保證了內存可見性
      • 若是變量沒有被lock,就不能被unlock
      • 對變量進行unlock以前,須要先把變量store、write進主內存
    • 對於volatile型變量的特殊規則
      • 保證此變量對全部線程的可見性
        • 能夠理解爲volatile變量是「寫直達」到主內存
        • 內存可見性沒法保證併發安全
          • 讀-判斷-改 類型問題
      • 禁止指令重排序
      • 每次使用V前都必須先從主內存刷新最新的值,用於保證能看見其餘線程對變量V所作的修改
      • 每次修改V後都必須馬上同步回主內存中,用於保證其餘線程能夠看到本身對變量V所作的修改。
    • 針對long和double型變量的特殊規則
      • 容許虛擬機實現自行選擇是否要保證64位數據類型的load、store、read和write這四個操做的原子性
      • 實際環境中,基本沒有出現
    • 原子性、可見性、有序性
      • 原子性
        • sychronized等
      • 可見性
        • volatile、synchronized、final
      • 有序性
        • 若是在本線程內觀察,全部的操做都是有序的;若是在一個線程中觀察另外一個線程,全部的操做都是無序
        • 指令重排、工做內存與主內存之間的緩衝
      • 先行發生規則
        • 程序次序規則
          • 控制流順序
        • 管程鎖定規則
          • 一個unlock操做先行發生於後面對同一個鎖的lock操做
        • volatile變量規則
          • volatile變量的寫操做先行發生於後面對這個變量的讀操做
        • 線程啓動規則
          • Thread對象的start()方法先行發生於此線程的每個動做
        • 線程終止規則
          • 線程中的全部操做都先行發生於對此線程的終止檢測
        • 對象中斷規則
          • 對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發
        • 對象終結規則
          • 一個對象的初始化完成(構造函數執行結束)先行發生於它的finalize()方法的開始。
        • 傳遞性
          • 若是操做A先行發生於操做B,操做B先行發生於操做C,那就能夠得出操做A先行發生於操做C的結論
  • Java與線程編程

    • 線程的實現數組

      • 內核線程實現(1:1)
        • 運行在內核態下的線程
        • 由內核線程支持的輕量級進程
          • 相關調度由內核線程控制,因此有些切入內核態的花費
      • 用戶線程實現(1:N)
        • 徹底創建在用戶空間上的
          • 線程的調度在用戶態上完成
          • 一個用戶進程對應多個用戶線程
      • 用戶線程加輕量級進程混合實現(N:M)
    • Java線程調度緩存

      • 協同式
        • 線程本身執行完了,纔回去通知其餘線程來
        • 調用時間由線程本身決定
      • 搶佔式
        • 線程執行時間由系統來分配
    • 狀態轉換安全

      • 新建併發

        • 建立後未啓動
      • 運行函數

        • 可能正在執行、可能正在等待執行
      • 無限期等待性能

        • 只能等待其餘線程顯示喚醒
          • 沒有設置時間參數的Object::wait()方法
          • 沒有設置Timeout參數的Thread:join()方法
          • LockSupport::park()方法
      • 限期等待優化

        • 能夠自身被喚醒
          • Thread::sleep()
          • 設置了Timeout參數的Object::wait()
          • 設置了Timeout參數的Thread::join()
          • LockSupport::parkNanos()方法
          • LockSupport::parkUntil()方法
      • 阻塞操作系統

        • 等待鎖的釋放
      • 結束

  • Java與協程

    • 協同的調度的輕量級用戶線程?
      • 狹隘,存在不是協同調度的輕量級用戶線程

線程安全與鎖優化

  • 什麼是線程安全

    • 當多個線程同時訪問一個對象時,若是不用考慮這些線程在運行時環境下的調度和交替執行,也不須要進行額外的同步,或者在調用方進行任何其餘的協調操做,調用這個對象的行爲均可以得到正確的結果,那就稱這個對象是線程安全的
  • 併發安全分級

    • 不可變、絕對線程安全、相對線程安全、線程兼容和線程對立

    • 不可變的對象是線程安全的

    • 絕對線程安全

      • 方法級別的很容易作到
      • 對象級別的基本沒有,強如vector,它的遍歷也不是線程安全的。
      • 相對線程安全
        • 只要求方法/操做級別的線程安全
      • 線程兼容
        • 能夠經過在調用端使用同步手段(鎖、原子操做)來保證在併發環境中安全使用
      • 線程對立
    • 線程安全的實現方法

      • 互斥同步(悲觀策略)
        • 互斥量、信號量、臨界區(操做系統概念)
        • synchronized
          • 非公平、不能超時退出、不能被中斷、鎖綁定一個條件
        • ReetrantLock
          • 公平、等待可中斷、鎖綁定多個條件
        • 二者性能差很少
      • 非阻塞同步(樂觀策略)
        • 硬件支持,必需要求衝突與修改操做能夠合併爲「一個原子操做」
          • Test-and-Set
          • Compare-and-Swap
          • Load-Linked/Store-Conditional
          • Swap
        • CAS
          • CAS指令須要有三個操做數,分別是內存位置(在Java中能夠簡單地理解爲變量的內存地址,用V表示)、舊的預期值(用A表示)和準備設置的新值(用B表示)。CAS指令執行時,當且僅當V符合A時,處理器纔會用B更新V的值,不然它就不執行更新。可是,不論是否更新了V的值,都會返回V的舊值,上述的處理過程是一個原子操做,執行期間不會被其餘線程中斷。
          • ABA問題
        • 無同步方案
          • 線程本地化存儲
            • ThreadLocal
              • ThreadLocalMap
              • 弱引用與內存泄露
  • 鎖優化

    • 自旋鎖與自適應鎖

    • 自旋鎖

      • 若是鎖被其餘線程佔用,就先spin一下
      • 競爭激烈的狀況下,效果很差
    • 自適應鎖

      • 由虛擬機來判斷是否要自旋,通常來講是根據自旋時間與阻塞的比值來的。
    • 鎖消除

      • 虛擬機會幫助判斷優化掉沒有意義的鎖
    • 鎖粗化

      • 適當放寬鎖範圍,有時候頻繁加鎖、釋放鎖
    • 輕量級鎖

      • 「對於絕大部分的鎖,在整個同步週期內都是不存在競爭的」
      • 但若是確實存在鎖競爭,除了互斥量的自己開銷外,還額外發生了CAS操做的開銷。所以在有競爭的狀況下,輕量級鎖反而會比傳統的重量級鎖更慢。
      • 就是每一個線程在嘗試得到鎖的時候,都會先進行CAS判斷,若是判斷成功(還沒人得到鎖),那就拿鎖,修改狀態。不然,阻塞,並將輕量鎖膨脹爲重量鎖
    • 偏向鎖

      • 偏向鎖就是在無競爭的狀況下把整個同步都消除掉,連CAS操做都不去作了

      • 某個線程得到了偏向鎖,就經過CAS把這個線程進行記錄,以後使用都不須要進行同步操做

        直到其餘線程嘗試獲取這個鎖,那麼偏向模式解除,要麼轉成未鎖定,要麼轉爲輕量鎖

相關文章
相關標籤/搜索