并發安全之鎖機制一

鎖機制一

鎖機制是計算機系統中解決并發沖突的核心工具,其存在和應用場景源于一個根本問題:當多個執行單元(線程、進程、分布式節點)同時訪問或修改同一份共享資源時,如何保證數據的正確性、一致性和系統可靠性?

一、為什么需要鎖?

想象以下場景,如果沒有鎖會發生什么:

  1. 銀行存款取款(數據競爭):
    • 線程A讀取賬戶余額:100元。
    • 線程B讀取賬戶余額:100元。
    • 線程A存入50元,計算新余額 100 + 50 = 150,寫入150。
    • 線程B取出30元,計算新余額 100 - 30 = 70,寫入70。
    • 結果: 最終余額是70元,而不是正確的120元 (100+50-30)。線程B的操作覆蓋了線程A的操作,因為兩者都基于舊的余額100元進行計算。這破壞了數據一致性。
  2. 訂票系統(超賣問題):
    • 剩余票數:1張。
    • 用戶A和用戶B同時點擊購買。
    • 服務器進程A讀取票數:1。
    • 服務器進程B讀取票數:1。
    • 進程A判斷有票,執行扣減:1-1=0,更新為0,出票成功。
    • 進程B判斷有票,執行扣減:1-1=0,更新為0,出票成功。
    • 結果: 1張票賣給了兩個人。這破壞了業務規則。
  3. 鏈表插入操作(數據結構損壞):
    • 鏈表: Node1 -> Node2。
    • 線程A要在Node1和Node2之間插入NodeX。
    • 線程B要刪除Node2。
    • 如果沒有鎖控制時序,可能導致:
      • 線程A剛讓Node1指向NodeX(但NodeX還沒指向Node2)。
      • 線程B刪除Node2,讓Node1(或前一個節點)指向Node2的下一個節點(可能是NULL)。
      • 結果:NodeX懸空或鏈表斷裂。這破壞了數據結構完整性。

這些問題的根源在于:

  • 非原子操作: 像“讀取-修改-寫入”這樣的復合操作,如果中間被其他操作打斷,就會導致結果錯誤。
  • 操作交錯的不可預測性: 多個操作以不同的順序和時機交織執行(交錯),會產生各種預期之外的結果。
  • 內存/緩存可見性問題: 一個線程對共享變量的修改可能不會立即被其他線程看到(由于CPU緩存的存在)。

鎖就是解決這些問題的“協調員”:

  1. 實現互斥: 鎖確保在同一時刻,只有一個執行單元能進入受保護的代碼區域(臨界區)訪問或修改特定的共享資源。其他執行單元必須等待鎖釋放。
  2. 保證原子性: 對于復合操作(如余額增減、票數扣減、鏈表節點指針修改),鎖可以將它們包裝成一個不可分割的操作單元,在執行過程中不會被其他操作打斷。
  3. 保障可見性: 在釋放鎖時,通常會強制將修改刷新到主內存;在獲取鎖時,通常會強制從主內存重新加載最新值。這確保了臨界區內修改的結果對后續獲得鎖的執行單元是可見的。
  4. 維持順序: 鎖隱式地建立了操作的先后順序,避免了破壞性交錯的產生。

二、鎖有哪些應用場景?

鎖的應用極其廣泛,存在于計算機系統的各個層面:

  1. 操作系統內核:
    • 保護內核數據結構: 進程表、文件描述符表、內存管理結構(如頁表、空閑列表)、設備驅動狀態等。多個CPU核心上的線程或中斷處理程序都需要安全地訪問這些全局結構。
    • 同步原語實現: 信號量、條件變量、屏障等同步機制本身就需要鎖(通常是自旋鎖)來保護其內部狀態。
    • 設備訪問: 確保同一時間只有一個進程/線程能向特定硬件設備(如打印機、特定端口)發送命令或數據。
  2. 多線程應用程序:
    • 保護共享內存變量: 計數器、標志位、配置參數等。
    • 保護復雜數據結構: 鏈表、哈希表、樹、隊列等。插入、刪除、查找(如果查找可能觸發修改)都需要鎖來防止結構損壞。
    • 單例模式實現: 確保在多線程環境下,一個類只有一個實例被創建(通常使用互斥鎖+雙重檢查鎖定)。
    • 線程池任務隊列: 多個工作線程從任務隊列取任務,生產者線程向隊列添加任務。隊列本身需要鎖保護。
    • 資源池管理: 如數據庫連接池、線程池。分配和回收資源需要互斥操作。
    • 緩存同步: 更新和讀取共享緩存數據時。
  3. 數據庫管理系統:
    • 事務并發控制: 這是數據庫鎖最核心的應用。數據庫使用各種粒度的鎖(行鎖、頁鎖、表鎖、意向鎖)和不同模式的鎖(共享鎖/S鎖、排他鎖/X鎖)來實現不同的事務隔離級別,保證ACID特性(特別是隔離性I和一致性C)。
      • 行鎖/記錄鎖: 防止兩個事務同時修改同一條記錄。
      • 間隙鎖: 防止幻讀(在范圍查詢中插入新記錄)。
      • 表鎖: 在特定操作(如ALTER TABLE)或行鎖沖突升級時使用。
      • 死鎖檢測與處理: 數據庫有專門的機制來處理事務間因循環等待鎖而導致的死鎖。
    • 索引維護: 對B+樹等索引結構進行分裂、合并等操作時需要鎖保護。
    • 緩存管理: 數據庫緩沖池(Buffer Pool)的管理也需要鎖機制。
  4. 文件系統:
    • 文件寫入: 防止多個進程同時寫入同一個文件導致內容混亂。通常使用文件鎖(如flock, fcntl)。
    • 元數據更新: 修改文件的屬性(如大小、權限、時間戳)、目錄結構(創建、刪除、重命名文件/目錄)時需要鎖保護,避免元數據不一致。
  5. 分布式系統:
    • 分布式鎖: 在多個獨立的服務器或進程之間協調對共享資源的訪問。例如:
      • 防止多個節點同時執行同一個定時任務。
      • 確保在分布式環境中對某個全局配置項的修改是互斥的。
      • 實現分布式環境下的選主(Leader Election)。
      • 控制對共享存儲(如分布式文件系統中的一個文件)的并發寫入。
      • 常用實現: ZooKeeper, Redis (RedLock), etcd, Consul 等提供的分布式鎖服務。這比單機鎖復雜得多,需要處理網絡延遲、節點故障、時鐘漂移等問題。

三、常見的鎖類型

  1. 互斥鎖:

    • 特點: 最基本的鎖類型。嚴格互斥,一次只允許一個持有者。
    • 行為: 如果一個線程試圖獲取已被持有的互斥鎖,它將被阻塞(進入睡眠狀態),直到鎖被釋放。操作系統會進行線程調度。
    • 用途: 保護需要絕對互斥訪問的臨界區。
    • 例子: pthread_mutex_t (POSIX), std::mutex (C++), synchronized 關鍵字修飾的方法或代碼塊 (Java 內部使用互斥鎖), Lock (數據庫中的排他鎖)。
  2. 自旋鎖:

    • 特點: 當嘗試獲取鎖失敗時,線程不會立即阻塞,而是在一個循環中不斷檢查鎖的狀態(“自旋”)。
    • 優點: 避免上下文切換的開銷,對于預期等待時間非常短的場景效率高。
    • 缺點: 浪費CPU周期(忙等待),如果等待時間長,效率極低。
    • 用途: 多處理器系統,臨界區非常小且執行時間極短,且持有鎖的線程不太可能被搶占的場景(如內核中斷處理)。
    • 例子: pthread_spinlock_t (POSIX), std::atomic_flag (C++ 可用于實現自旋鎖), 底層硬件指令如 test-and-set, compare-and-swap
  3. 讀寫鎖:

    • 特點: 區分讀操作和寫操作。
      • 讀鎖: 允許多個讀者同時持有。只要沒有寫者,讀者就可以并發訪問。
      • 寫鎖: 是排他的。一個寫鎖被持有時,不能有其他讀者或寫者。獲取寫鎖通常需要等待所有現有的讀者釋放讀鎖。
    • 優點: 大幅提高讀多寫少場景的并發性能。
    • 缺點: 實現比互斥鎖復雜;如果寫操作頻繁,可能導致讀者或寫者“餓死”(需要公平策略)。
    • 用途: 保護經常被讀取但較少被修改的數據結構(如配置信息、緩存)。
    • 例子: pthread_rwlock_t (POSIX), std::shared_mutex (C++17), ReentrantReadWriteLock (Java), LOCK IN SHARE MODE / FOR SHARE (數據庫共享鎖), FOR UPDATE (數據庫排他鎖)。
  4. ?悲觀鎖 vs 樂觀鎖?

    ?類型??機制??適用場景?
    ?悲觀鎖?默認資源會被修改,訪問前強制加鎖(如行鎖、表鎖)寫操作頻繁的高沖突場景?
    ?樂觀鎖?通過版本號或CAS算法檢測沖突,提交時校驗讀多寫少的低沖突場景(如電商庫存)?

CAS(Compare-And-Swap) 是一種關鍵的無鎖(Lock-Free)編程原子操作,也是實現樂觀并發控制的核心。它直接由大多數現代CPU提供硬件支持(通過特定的機器指令),用于在多線程/多處理器環境下安全地更新共享變量,而無需使用傳統的互斥鎖。

工作流程(從線程視角)

  1. 讀取: 線程讀取共享變量 V 的當前值,記為 current_value
  2. 計算: 線程基于 current_value 計算出希望更新的新值 new_value
  3. CAS嘗試: 線程執行 CAS(V, current_value, new_value)
    • 成功: 如果 V 的當前值仍然等于 current_value(意味著在此期間沒有其他線程修改過 V),則 V 被原子地設置為 new_value,返回 true。線程的更新操作完成。
    • 失敗: 如果 V 的當前值不等于 current_value(意味著在此期間有其他線程搶先修改了 V),則 CAS 什么也不做(不更新 V),返回 false
  4. 失敗處理: 如果 CAS 失敗,線程通常不會阻塞,而是選擇:
    • 放棄: 如果操作允許失敗。
    • 重試(自旋): 最常見的方式。線程回到步驟1,重新讀取 V最新值作為新的 current_value,重新計算 new_value,然后再次嘗試 CAS。這個循環(讀取 -> 計算 -> CAS)會一直持續,直到 CAS 成功或達到某種退出條件(如重試次數上限)。

四、golang中的鎖機制

在Go語言中,處理并發問題時通常優先考慮使用信道(channel),但在某些情況下,當信道無法解決問題時,就需要使用鎖機制來處理共享內存的并發訪問。Go語言提供了兩種主要的鎖類型:互斥鎖(Mutex)和讀寫鎖(RWMutex)。

1. 互斥鎖(sync.Mutex)
  • 作用:確保同一時間只有一個goroutine訪問共享資源。
  • 方法
    • Lock():獲取鎖(若鎖被占用,則阻塞當前goroutine)
    • Unlock():釋放鎖
  • 特點
    • 不可重入:同一goroutine重復加鎖會導致死鎖。
    • 零值可用:未初始化的Mutex可直接使用。
var mu sync.Mutex
var counter intfunc increment() {mu.Lock()          // 加鎖defer mu.Unlock()  // 確保解鎖(推薦用defer避免忘記解鎖)counter++
}
2. 讀寫鎖(sync.RWMutex)
  • 適用場景:讀多寫少(如緩存系統)。
  • 方法
    • RLock():獲取讀鎖(允許多個讀并發)
    • RUnlock():釋放讀鎖
    • Lock():獲取寫鎖(獨占,與讀/寫互斥)
    • Unlock():釋放寫鎖
  • 規則
    • 寫鎖優先級高于讀鎖(防止讀鎖餓死寫鎖)
    • 持有讀鎖時無法升級為寫鎖
var rwMu sync.RWMutex
var data map[string]stringfunc read(key string) string {rwMu.RLock()         // 讀鎖defer rwMu.RUnlock()return data[key]
}func write(key, value string) {rwMu.Lock()          // 寫鎖defer rwMu.Unlock()data[key] = value
}
3. Mutex的優化機制
  • 自旋嘗試
    當鎖被短期持有時,等待的goroutine會在用戶態自旋嘗試(約4次),避免立即進入休眠(減少上下文切換開銷)。
  • 饑餓模式
    若某個goroutine等待超過1ms,鎖會進入饑餓模式——新來的goroutine直接排隊(不搶鎖),確保公平性。
4. RWMutex的設計
  • 讀鎖計數
    通過readerCount跟蹤當前讀鎖數量(正數表示讀鎖,負數表示有寫鎖等待)。
  • 寫鎖優先
    當有寫鎖等待時,新來的讀鎖會被阻塞,防止寫鎖被餓死。

五、mysql的鎖機制

1. 全局鎖(Global Lock)
  • 作用:鎖定整個數據庫實例,使所有表處于只讀狀態。
  • 命令FLUSH TABLES WITH READ LOCK(FTWRL)25。
  • 場景:全庫邏輯備份(如mysqldump)時確保數據一致性。
  • 風險:阻塞所有寫操作,長時間鎖定會導致業務癱瘓。推薦事務引擎使用–single-transaction參數(基于MVCC備份,不阻塞寫)24。
2. 表級鎖(Table Lock)
  • 類型
    • 普通表鎖:通過LOCK TABLES ... READ/WRITE手動加鎖,讀鎖允許多會話并發讀但阻塞寫,寫鎖獨占36。
    • 元數據鎖(MDL):自動加鎖,保護表結構。DML操作(如SELECT)加MDL讀鎖,DDL操作(如ALTER TABLE)加MDL寫鎖,讀寫互斥。長事務會阻塞DDL,導致雪崩24。
    • 意向鎖(Intention Lock):表級鎖,分為意向共享鎖(IS)和意向排他鎖(IX),用于快速判斷表中是否有行鎖沖突48。
  • 適用引擎:MyISAM默認表鎖;InnoDB顯式支持。
  • 特點:開銷小、加鎖快、無死鎖,但并發度低39。
3. 行級鎖(Row Lock)
  • 適用引擎:僅InnoDB支持,細粒度鎖定單行數據。
  • 特點:開銷大、加鎖慢、可能出現死鎖,但并發度高16。
  • 加鎖條件:必須通過索引檢索數據,否則退化為表鎖310。
4.共享鎖(S鎖)

共享鎖(S鎖)SELECT ... LOCK IN SHARE MODE,允許多事務讀,阻塞寫。

允許多事務?并發讀取同一數據?,但阻止任何事務獲取排他鎖進行修改?

鎖兼容性:多個S鎖可共存,但S鎖與X鎖互斥?

5.排他鎖(X鎖)

排他鎖(X鎖)SELECT ... FOR UPDATE或自動加鎖(如UPDATE),獨占數據

事務持有X鎖時,?禁止其他事務加任何鎖?(包括S鎖和X鎖),實現獨占讀寫?

自動應用于寫操作:UPDATEDELETEINSERT語句默認加X鎖?

6.悲觀鎖(Pessimistic Lock)
  1. ?實現機制?
    • 基于?數據庫原生鎖機制?(S鎖/X鎖),操作前先加鎖,假設高并發沖突概率?。
    • 典型語句:SELECT ... FOR UPDATE(X鎖)、SELECT ... LOCK IN SHARE MODE(S鎖)?。
  2. ?適用場景?
    • 寫密集型操作(如庫存扣減、支付交易)?。
    • 需強一致性的金融系統,容忍鎖開銷換取安全性?。
7.樂觀鎖(Optimistic Lock)
  1. ?實現機制?

    • ?無鎖設計?:通過業務層邏輯(版本號/時間戳)檢測沖突,提交時校驗數據一致性?89。

    • 偽代碼邏輯:

      sql CodeUPDATE products 
      SET stock = new_stock, version = version + 1 
      WHERE id = 10 AND version = current_version; -- 校驗版本號
      
  2. ?適用場景?

    • 讀多寫少場景(如評論計數更新)?。
    • 分布式系統,減少數據庫鎖競爭開銷?。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90987.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90987.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90987.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

結合項目闡述 設計模式:單例、工廠、觀察者、代理

原文鏈接:https://download.csdn.net/blog/column/12433305/133862792#_1613 1、工廠模式應用 C17及之后可編譯 /*日志落地模塊的實現1.抽象落地基類2.派生子類(根據不同落地方向進行派生)3.使用工廠模式進行創建與表示的分離 */#ifndef _…

uniapp 更新apk有緩存點不動,卸載安裝apk沒有問題。android

方式一。pages.json:"globalStyle" : {"navigationBarTextStyle" : "black","navigationBarTitleText" : "uni-app","navigationBarBackgroundColor" : "#F8F8F8","backgroundColor&qu…

HTML響應式SEO公司網站源碼

核心優勢 100%純HTML/CSS開發自動適配手機/平板/PC內置SEO優化結構0.5秒極速加載 包含頁面 ? 首頁(關鍵詞布局優化版) ? 服務項目展示頁 ? 客戶案例庫 ? 新聞資訊系統 ? 聯系方式(帶地圖API) 技術參數 兼容Chrome/Firefo…

Error: llama runner process has terminated: exit status 2

我是i7 12700h ,3080顯卡,在 Windows 11 上運行 ollama run deepseek-r1:1.5b 出現 Error: llama runner process has terminated: exit status 2 之前是好用的,后來不知為什么就不好用了。 原因: 檢查 Microsoft Visual C Redistributab…

Linux中ssh遠程登錄原理與配置

SSH連接的五個階段 1. 版本協商階段(Protocol Version Negotiation)目的:協商使用SSH-1或SSH-2協議(現代系統默認SSH-2)。流程:關鍵點:若版本不兼容(如客戶端只支持SSH-1&#xff0c…

Kubernetes --存儲入門

一、Volume 的概念對于大多數的項目而言,數據文件的存儲是非常常見的需求,比如存儲用戶上傳的頭像、文件以及數據庫的數據。在 Kubernetes 中,由于應用的部署具有高度的可擴展性和編排能力(不像傳統架構部署在固定的位置&#xff…

螞蟻 KAG 框架開源:知識圖譜 + RAG 雙引擎

引言:從RAG到KAG,專業領域知識服務的技術突破 在大語言模型(LLM)應用落地過程中,檢索增強生成(RAG) 技術通過引入外部知識庫有效緩解了模型幻覺問題,但在專業領域仍面臨三大核心挑戰…

V-Ray 7.00.08 for 3ds Max 2021-2026 安裝與配置教程(含語言補丁)

本文介紹 V-Ray 7.00.08 渲染器在 3ds Max 2021-2026 各版本中的安裝與使用配置步驟,適合需要進行可視化渲染工作的設計師、建筑師及相關從業者。附帶語言補丁配置方式,幫助用戶獲得更順暢的使用體驗。 📁 一、安裝文件準備 軟件名稱&#xf…

Go-Elasticsearch Typed Client查詢請求的兩種寫法強類型 Request 與 Raw JSON

1 為什么需要兩種寫法? 在 Golang 項目中訪問 Elasticsearch,一般會遇到兩類需求:需求場景特點最佳寫法后臺服務 / 業務邏輯查詢固定、字段清晰,需要編譯期保障Request 結構體儀表盤 / 高級搜索 / 模板 DSL查詢片段由前端或腳本動…

Leaflet 綜合案例-聚類圖層控制

看過的知識不等于學會。唯有用心總結、系統記錄,并通過溫故知新反復實踐,才能真正掌握一二 作為一名摸爬滾打三年的前端開發,開源社區給了我飯碗,我也將所學的知識體系回饋給大家,助你少走彎路! OpenLayers…

React組件中的this指向問題

在 React 組件中,函數定義方式影響this指向的核心原因是箭頭函數與普通函數的作用域綁定規則不同,具體差異如下:? 1. 普通函數(function定義)需要手動bind(this)的原因? 當用function在組件內定義方法時&#xff1…

Vue 項目中的組件引用如何實現,依賴組件間的數據功能交互及示例演示

在 Vue 項目中,組件間的引用與數據交互是核心功能之一。以下是組件引用和數據交互的詳細實現方式及示例:一、組件引用方式 1. 基本組件引用 局部注冊:在父組件中按需引入子組件并注冊。 // ParentComponent.vue import ChildComponent from .…

? 使用 Flask 實現頭像文件上傳與加載功能

文章目錄&#x1f9f1; 技術棧&#x1f5c2;? 項目結構與配置&#x1f510; 用戶身份校驗邏輯&#x1f4e4; 頭像上傳接口&#xff1a;/file/avatar/upload&#x1f4e5; 加載頭像接口&#xff1a;/file/avatar/load/<filename>&#x1f9ea; 示例請求&#xff08;使用 …

去除視頻字幕 5: 使用 ProPainter, 記錄探索過程

使用 ProPainter 去除視頻上的字幕&#xff0c;效果演示&#xff08;比之前好多了。&#xff09;。 1. 項目目標 去除視頻 (bear.webm) 中的硬字幕。 2. 初始嘗試與關鍵失敗&#xff1a;IOPaint 方法: 使用 IOPaint&#xff08;一個圖像修復工具&#xff09;配合 PaddleOCR 逐…

JavaScript HTTP 請求:從老古董到新潮流

前端開發離不開跟后端打交道&#xff0c;HTTP 請求就是這座橋梁。JavaScript 提供了好幾種方式來發請求&#xff0c;從老牌的 XMLHttpRequest (XHR) 到現代的 Fetch API&#xff0c;再到各種好用的第三方庫&#xff08;像 Axios、Ky、Superagent&#xff09;。咱們一個一個聊清…

Windows10系統使用Cmake4.1.0構建工具+Visual Studio2022編譯Opencv4.11教程

安裝提示 后續安裝本Cmake和Opencv版本及以上都可以。Microsoft Visual Studio2022已默認安裝&#xff0c;沒有安裝給出教程鏈接。 一、Cmake4.1.0下載 1.官網下載&#xff1a;https://cmake.org/download/&#xff0c;找到cmake-4.1.0-rc3-windows-x86_64.zip版本 2.壓縮包…

【性能測試】Jmeter+Grafana+InfluxDB+Prometheus Windows安裝部署教程

一、工具作用與整體架構 1.1 各工具核心作用 工具作用描述關鍵特性Jmeter性能測試工具&#xff0c;模擬多用戶并發請求&#xff0c;生成測試數據支持HTTP/HTTPS、數據庫等多種協議&#xff0c;可自定義測試場景InfluxDB時序數據庫&#xff0c;專門存儲時間序列數據&#xff0…

【Kubernetes】使用Deployment進行的資源調度,資源清理,伸縮與更新管控

Kubernetes Deployment 實戰&#xff1a;從資源清理到伸縮與更新管控 一、基礎準備&#xff1a;清理閑置 ReplicaSet 在使用 Deployment 時&#xff0c;每次更新都會生成新的 ReplicaSet&#xff08;簡稱 RS&#xff09;&#xff0c;舊的 RS 會被保留但設置為 DESIRED0。這些閑…

stm32使用USB虛擬串口,因電腦缺少官方驅動而識別失敗(全系列32單片機可用)

驅動下載地址 官網地址&#xff1a;https://www.st.com/en/development-tools/stsw-stm32102.html

枚舉中間位置基礎篇

參考資料來源靈神在力扣所發的題單&#xff0c;僅供分享學習筆記和記錄&#xff0c;無商業用途。 核心思路&#xff1a; 一&#xff1a;直接直接用數據結構記錄需要的數據&#xff0c;在枚舉右&#xff0c;維護左的循環中&#xff0c;刪除當前位置的元素即可達成一樣效果 二…