Java 鎖機制總結

来源:https://www.cnblogs.com/Melles/archive/2019/04/04/10657611.html
-Advertisement-
Play Games

鎖的種類 獨享鎖 VS 共用鎖 獨享鎖:鎖只能被一個線程持有(synchronized) 共用鎖:鎖可以被多個程式所持有(讀寫鎖) 樂觀鎖 VS 悲觀鎖 樂觀鎖:每次去拿數據的時候都樂觀地認為別人不會修改,所以不進行加鎖操作。樂觀鎖適用於多讀的應用類型。(CAS,Atomic) CAS(Compar ...


鎖的種類

  • 獨享鎖 VS 共用鎖
    • 獨享鎖:鎖只能被一個線程持有(synchronized)
    • 共用鎖:鎖可以被多個程式所持有(讀寫鎖)
  • 樂觀鎖 VS 悲觀鎖
    • 樂觀鎖:每次去拿數據的時候都樂觀地認為別人不會修改,所以不進行加鎖操作。樂觀鎖適用於多讀的應用類型。(CAS,Atomic)
      • CAS(Compare And Swap),其思想是:我認為V的值應該為 A,如果是,那麼將 V 的值更新為 B,否則不修改並告訴V的值實際為多少。這樣一來當有多個線程嘗試修改同一個對象時,只有一個線程能夠成功修改,因為一旦有一個線程修改成功了,那麼其他線程就沒法滿足 V 的值是 A 了。其他線程修改失敗之後不會被掛起,而是再次嘗試修改。
    • 悲觀鎖:總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖。
  • 公平鎖 VS 非公平鎖
    • 公平鎖:對於等待鎖隊列中的線程,按照先來先得的原則,給予鎖。
    • 非公平鎖:一個線程一執行到需要鎖的地方,就嘗試去要鎖,失敗了再進等待隊列。這樣一來,就能夠大大減少喚醒線程的開銷,但有可能會出現某線程餓死的情況。
      • synchronized 是非公平鎖,lock 可以是公平也可以非公平。
  • 其他概念
    • 可重入鎖(synchronized 和 lock):可重入性表明瞭鎖的分配不是基於方法而是基於線程的。如:synchronized 了兩個方法 method1, method2,method1 中調用了 method2,那麼只要線程獲得了 method1 的鎖,那麼它就一定能夠進入 method2 而不需再申請鎖。
    • 可中斷鎖(lockInterruptible()):對於等待鎖的線程,若等待時間過長,想中斷該等待過程,讓該線程去做其他的事,那麼就需要可中斷鎖。

Java 鎖的狀態

Java 鎖狀態的級別從低到高為:無鎖狀態、偏向鎖狀態、輕量級鎖狀態、重要級鎖狀態,而鎖的狀態是被記錄在 Java 對象的對象頭中。

  • Java 的對象頭:由 Mark Word、指向類的指針、數組長度,其中 Mark Word 負責記錄有關鎖的信息

    • 32 位 JVM 中 Mark Word 存儲的內容為

      鎖狀態 25bit 4bit 1bit (是否是偏向鎖) 2bit (鎖標誌位)
      無鎖 對象的 HashCode 分代年齡 0 01
      偏向鎖 線程 ID 與 Epoch 分代年齡 1 01
      輕量級鎖 指向棧中的鎖記錄指針 同前 同前 00
      重量級鎖 指向重量級鎖的指針 同前 同前 10
      GC 標記 11
  • 偏向鎖、輕量級鎖概念的出現,都是為了減少同步喚醒的代價,若所有鎖都為重量級的,那麼所有等待鎖的線程必須進入阻塞態

    • 偏向鎖:大多數情況下,鎖並不存在被大量線程競爭,而且總是由一個線程多次重覆申請,那麼這時只需要讓偏向鎖記錄該線程 ID,這樣當該線程又來聲請鎖的時候,就能快速獲得鎖了。
    • 輕量級鎖:存在競爭,但是不強力且持有鎖的線程會很快釋放鎖,在這種情況下,等待鎖的線程可以處於自旋狀態,而不必進入阻塞狀態。
  • Java 中鎖的狀態是一級一級往上升的,具體情況為:

    • 當未遇到同步代碼塊(即沒有出現鎖的時候),處於無鎖狀態。
    • 當對象被當作了同步鎖,但當前只有一個線程 A 申請了該鎖(沒有競爭產生),那麼對象的鎖狀態就會升級為偏向鎖。
    • 當又有另一線程 B 來申請鎖時,這時會檢查當前占用該鎖的線程 A 是否處於活躍狀態或者仍然需要該鎖,即是否有競爭會產生。若無,則偏向鎖偏向 B,A 釋放偏向鎖。
    • 若有,那麼偏向鎖就會升級會輕量級鎖。但若競爭進一步加大(有很多線程需求鎖,且占用時間較長),那麼鎖就會進一步升級為重量級鎖。

Java 鎖機制的實現


synchronized

從上一節我們可以看出來,Java 中能夠作為鎖的只能是對象,所以 synchronized 的實現,也是通過對對象進行加鎖實現的。

  • synchronized 的三種用法
    • synchronized aMethod,鎖一個類的方法,可以防止多個線程同時調用同一個對象的這個方法,但是能夠同時調用。
    • static synchronized aMethod,鎖一個類的靜態方法,可以防止多個線程同時這個類的該靜態方法。
    • synchronized (object){代碼塊},把 object 對象當作該代碼塊的鎖
  • synchronized 使用起來簡單,但是有如下缺點
    • 不支持中斷,可能造成其他線程等待很長的時間,甚至死鎖。
    • 不支持讀寫鎖,即讀讀操作是能夠同時進行的,但是 synchronized 沒法實現。

Lock

Lock 是 java.util.concurrent.locks 包里的一個介面,對應的 ReentrantLock 類實現了該介面。

  • 一般使用方法

    public class LockTest {
        private Lock alock = new ReentrantLock(); // 鎖需要定義在具體使用鎖的棧幀的上一層,即若線上程里定義的話,不同的線程會 new 出不同的鎖,這樣就不能實現同步了。
    
        public static void main(String[] args) {
            Runnable r = () -> {
                alock.lock(); // 獲得鎖
                try {
                    // do something
                } catch(Exception e){
    
                } finally{
                    alock.unlock();
                }
            }
            Thread test = new Thread(r);
            test.start();
        }
    }
  • 獲得鎖的方法

    • lock(): 獲取鎖,若失敗,則等待
    • tryLock(long time, TimeUnit unit): 嘗試獲得鎖,返回一個布爾值,表明是否獲得到了鎖。
    • lockInterruptibly(): 若某一個線程使用這個方法等待獲取鎖,那麼可以通過 thread.interrupt() 的方法去讓他中斷等待,乾別的事。
  • 除此之外,java.util.concurrent.locks 包內還定義有 ReadWriteLock 介面,並有多種讀寫鎖的實現類。


volatile

一種輕量級同步機制,能夠保證 volatile 修飾的變數具有可見性,但不具備原子性

  • 原子性與可見性
    • 原子性:在多線程併發的條件下,對於變數的操作是線程安全的,不會受到其他線程的干擾。Atomatic 基於底層硬體處理器提供的原子指令,保證併發時線程安全。
    • 可見性:在多線程併發的條件下,對於變數的修改,其他線程中能獲取到修改後的。volatile 通過對於值的操作,會立即更新到主存中,當其他線程獲取該值會從主存中獲取。
  • 使用方法與 synchronized 一樣,都是 Java 關鍵詞。

Atomatic

前面提到過 Atomatic 是一種樂觀鎖,它是通過操作的原子性來保證自己的線程安全的,即操作是不可被打斷的,且具有排他性。當某一線程進行該原子操作時,其他想要執行該操作的線程只能處於自旋狀態,等待該操作完成。

  • 其實,這個給人的感覺,atomatic 有點像輕量級鎖,不過這種線程的等待,不是通過軟體實現的,而是通過硬體實現的(硬體上的原子操作)。
  • 不過這種依靠硬體上的原子操作實現的鎖機制,導致 atomatic 的相關操作都比較基礎。
  • 使用方法:參見 java.util.concurrent.atomic

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 經過前兩個模式的學習,是不是對設計模式有了進一步的認識了呢,現在,我們繼續沖鴨。 本章可以稱為“給愛用繼承的人一個全新的設計眼界”。這裡我們即將再度探討典型的繼承濫用問題,我們將學到如何使用對象組合的方式,做到在運行時裝飾類。為什麼呢?一旦熟悉了裝飾的技巧,你將能夠在不修改任何底層代碼的情況下,給對 ...
  • 多線程 什麼是線程? - 能獨立運行的基本單位——線程(Threads)。 - 線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。 - 一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線程並行執行不同的任務。 - 就好比生產的工廠,一個車 ...
  • 前言 開心一刻 女兒: “媽媽,你這麼漂亮,當年怎麼嫁給了爸爸呢?” 媽媽: “當年你爸不是窮嘛!‘ 女兒: “窮你還嫁給他!” 媽媽: “那時候剛剛畢業參加工作,領導對我說,他是我的扶貧對象,我年輕理解錯了,就嫁給他了!” 女兒...... @Import註解應用 應用開發中,當我們的功能模塊比較 ...
  • 列表推導式和生成表達式有相似,但是卻有本質不不同簡單的把它們之間的關係理一理吧:# 列表推導式: res=[i for i in range(6)] print(res) 結果:[0, 1, 2, 3, 4, 5] # 生成表達式: res=(i for i in range(6)) print(r ...
  • Python 簡介 Python 是一個高層次的結合瞭解釋性、編譯性、互動性和麵向對象的腳本語言。 Python 的設計具有很強的可讀性,相比其他語言經常使用英文關鍵字,其他語言的一些標點符號,它具有比其他語言更有特色語法結構。 Python 是一個高層次的結合瞭解釋性、編譯性、互動性和麵向對象的腳 ...
  • Python基礎數據類型之int,str,bool及常用方法 ...
  • 回到上次編輯位置 Ctrl + Alt + <- (向後) Ctrl + Alt + -> (向前) 這個快捷鍵有時和電腦桌面快捷鍵衝突。解決辦法: win + D 回到電腦桌面,右鍵—>圖像選項—>快捷鍵—>禁用 ...
  • 死磕 java集合之TreeMap源碼分析(三) 紅黑樹刪除元素的時間複雜度如何? 為什麼刪除元素之後要做平衡? 以什麼樣的形式平衡最省時間? ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...