如何優雅使用 ReentrantLock 進行加解鎖:避免常見坑點,提高代碼可維護性

引言:鎖的基本概念和問題

在多線程編程中,為了確保多個線程在訪問共享資源時不會發生沖突,我們通常需要使用 來同步對資源的訪問。Java 提供了不同的鎖機制,其中 ReentrantLock 是一種最常用且功能強大的鎖,它屬于 java.util.concurrent 包,并提供了比 synchronized 更加靈活的鎖控制。

盡管 ReentrantLock 提供了許多優點,但不當使用鎖可能會導致死鎖、性能下降或者是不可維護的代碼。本文將深入探討如何優雅地使用 ReentrantLock,避免常見的坑點,并提升代碼的可維護性。


一、ReentrantLock 的基本概念

在討論如何優雅使用 ReentrantLock 之前,先來快速回顧一下它的基本概念。

ReentrantLock 是 Java 提供的一個顯式鎖,它比 synchronized 提供了更高的靈活性。與 synchronized 鎖相比,ReentrantLock 提供了以下優勢:

  • 可重入性:一個線程可以多次獲得同一把鎖,而不會被阻塞。
  • 可中斷的鎖請求:使用 lockInterruptibly 方法可以使線程在等待鎖時響應中斷。
  • 公平性:可以選擇公平鎖(FIFO 隊列)或者非公平鎖,避免了線程饑餓問題。
  • 手動解鎖:通過 unlock 方法來釋放鎖,可以精確控制鎖的釋放時機。

二、ReentrantLock 的使用基本模式

我們來看看如何使用 ReentrantLock 加鎖和解鎖。

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {Runnable task = () -> {lock.lock();  // 獲取鎖try {// 執行臨界區代碼System.out.println(Thread.currentThread().getName() + " is processing the task.");} finally {lock.unlock();  // 確保解鎖}};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();}
}
如何理解:
  • lock.lock() 會嘗試獲取鎖。如果鎖已被其他線程持有,當前線程將會被阻塞。
  • unlock() 用來釋放鎖,必須放在 finally 塊中,確保鎖的釋放即使在出現異常的情況下也能執行。

三、如何優雅地處理 ReentrantLock 的加鎖和解鎖?

雖然 ReentrantLock 提供了靈活性,但錯誤的使用方式會帶來死鎖和資源泄漏等問題。為了避免這些問題,我們可以遵循以下最佳實踐:

1. 使用 finally 塊確保解鎖

最常見的錯誤是,忘記釋放鎖導致死鎖,或者釋放鎖時拋出異常。為了保證鎖的釋放,即使發生異常,也應始終在 finally 塊中解鎖。

lock.lock();
try {// 執行臨界區代碼
} finally {lock.unlock();  // 確保鎖的釋放
}
2. 使用 lockInterruptibly 實現中斷鎖請求

在某些情況下,線程可能在獲取鎖時被掛起較長時間,無法及時響應中斷。通過使用 lockInterruptibly,我們可以確保線程在等待鎖時響應中斷。

public void safeMethod() {try {lock.lockInterruptibly();  // 可以響應中斷// 執行臨界區代碼} catch (InterruptedException e) {Thread.currentThread().interrupt();  // 處理中斷System.out.println("Thread was interrupted while waiting for the lock.");} finally {lock.unlock();}
}
3. 使用 tryLock 避免阻塞

ReentrantLock 還提供了 tryLock 方法,它嘗試獲取鎖并立即返回。如果無法獲取鎖,線程不會被阻塞,而是返回 false,讓我們可以采取其他措施(比如重試或跳過操作)。

if (lock.tryLock()) {try {// 執行臨界區代碼} finally {lock.unlock();}
} else {// 鎖獲取失敗,可以選擇重試或執行其他操作System.out.println("Could not acquire lock. Try again later.");
}
4. 使用公平鎖避免線程饑餓

默認情況下,ReentrantLock 是非公平鎖,這意味著線程獲取鎖的順序沒有嚴格的先后順序。若希望線程按請求鎖的順序獲取鎖(避免線程饑餓),可以創建一個公平鎖。

ReentrantLock fairLock = new ReentrantLock(true);  // 公平鎖

使用公平鎖可能會導致性能稍微下降,因為線程需要按照隊列順序獲得鎖,但它能避免某些線程長期無法獲取鎖的情況。


四、避免死鎖的技巧

死鎖 是多線程編程中最常見的問題之一,它發生在兩個或更多線程因為相互等待對方釋放鎖而導致無法繼續執行的情況。為了避免死鎖,我們可以遵循以下幾點:

1. 鎖的獲取順序

確保所有線程都按照相同的順序獲取鎖。例如,如果線程 A 需要獲取鎖 X 和鎖 Y,則線程 B 也應該按照相同的順序獲取鎖 X 和鎖 Y,避免出現互相等待的情況。

2. 使用 tryLock 避免無限等待

當線程無法獲取鎖時,使用 tryLock 方法可以避免線程陷入無限等待的狀態,給線程設置一個超時時間。

if (lock1.tryLock() && lock2.tryLock()) {try {// 執行臨界區代碼} finally {lock1.unlock();lock2.unlock();}
} else {// 鎖獲取失敗,執行其他邏輯System.out.println("Could not acquire both locks, retrying...");
}

通過設置超時時間,如果兩把鎖無法在指定時間內獲取,線程將放棄等待,避免死鎖。


五、總結:優雅使用 ReentrantLock 的最佳實踐

ReentrantLock 是一種非常強大的工具,能夠為我們提供比 synchronized 更加細粒度的鎖控制。然而,要優雅地使用它,需要遵循以下幾個最佳實踐:

  1. 確保鎖的釋放:總是將 unlock 放入 finally 塊中,確保即使出現異常,鎖也能被釋放。
  2. 使用 lockInterruptibly:在可能會被長時間阻塞的場景中使用 lockInterruptibly 來響應中斷。
  3. 使用 tryLock:避免線程因無法獲取鎖而無限阻塞,通過 tryLock 來檢測鎖的狀態,做出相應處理。
  4. 使用公平鎖:在需要保證鎖的公平性時使用公平鎖,避免線程饑餓現象。
  5. 避免死鎖:通過統一的鎖獲取順序、合理使用 tryLock 來避免死鎖。

通過遵循這些原則,我們可以在使用 ReentrantLock 時避免常見的坑點,提高代碼的穩定性和可維護性,編寫更加優雅的多線程代碼。

推薦閱讀文章

  • 由 Spring 靜態注入引發的一個線上T0級別事故(真的以后得避坑)

  • 如何理解 HTTP 是無狀態的,以及它與 Cookie 和 Session 之間的聯系

  • HTTP、HTTPS、Cookie 和 Session 之間的關系

  • 什么是 Cookie?簡單介紹與使用方法

  • 什么是 Session?如何應用?

  • 使用 Spring 框架構建 MVC 應用程序:初學者教程

  • 有缺陷的 Java 代碼:Java 開發人員最常犯的 10 大錯誤

  • 如何理解應用 Java 多線程與并發編程?

  • 把握Java泛型的藝術:協變、逆變與不可變性一網打盡

  • Java Spring 中常用的 @PostConstruct 注解使用總結

  • 如何理解線程安全這個概念?

  • 理解 Java 橋接方法

  • Spring 整合嵌入式 Tomcat 容器

  • Tomcat 如何加載 SpringMVC 組件

  • “在什么情況下類需要實現 Serializable,什么情況下又不需要(一)?”

  • “避免序列化災難:掌握實現 Serializable 的真相!(二)”

  • 如何自定義一個自己的 Spring Boot Starter 組件(從入門到實踐)

  • 解密 Redis:如何通過 IO 多路復用征服高并發挑戰!

  • 線程 vs 虛擬線程:深入理解及區別

  • 深度解讀 JDK 8、JDK 11、JDK 17 和 JDK 21 的區別

  • 10大程序員提升代碼優雅度的必殺技,瞬間讓你成為團隊寵兒!

  • “打破重復代碼的魔咒:使用 Function 接口在 Java 8 中實現優雅重構!”

  • Java 中消除 If-else 技巧總結

  • 線程池的核心參數配置(僅供參考)

  • 【人工智能】聊聊Transformer,深度學習的一股清流(13)

  • Java 枚舉的幾個常用技巧,你可以試著用用

  • 由 Spring 靜態注入引發的一個線上T0級別事故(真的以后得避坑)

  • 如何理解 HTTP 是無狀態的,以及它與 Cookie 和 Session 之間的聯系

  • HTTP、HTTPS、Cookie 和 Session 之間的關系

  • 使用 Spring 框架構建 MVC 應用程序:初學者教程

  • 有缺陷的 Java 代碼:Java 開發人員最常犯的 10 大錯誤

  • Java Spring 中常用的 @PostConstruct 注解使用總結

  • 線程 vs 虛擬線程:深入理解及區別

  • 深度解讀 JDK 8、JDK 11、JDK 17 和 JDK 21 的區別

  • 10大程序員提升代碼優雅度的必殺技,瞬間讓你成為團隊寵兒!

  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)

  • 為什么用了 @Builder 反而報錯?深入理解 Lombok 的“暗坑”與解決方案(二)

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

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

相關文章

Redhat紅帽 RHCE8.0認證體系課程

課程大小:7.7G 課程下載:https://download.csdn.net/download/m0_66047725/90546064 更多資源下載:關注我 紅帽企業 Linux 系統的管理技能已經成為現代數據中心的核心競爭力。 Linux 在支持混合云、跨物理服務器、虛機、私有云和公共云計…

Shell腳本編程

目錄 1. Shell腳本概述 什么是Shell? Shell的作用 常見的Shell類型 2. 環境搭建與安裝 Linux系統 macOS系統 Windows系統 3.安裝并配置Zsh(macOS/Linux) 4. Shell基礎語法 變量與數據類型 輸入交互 5. Shell腳本進階 進程管理 …

學生管理系統(Python)

運行結果: 源代碼: """ 項目:類似于學生管理系統---增刪改查 """ #封裝一個學生類 import random class Student: def __init__(self,stuid,name,score): self.stuid stuid self.name name self.score …

電商素材革命:影刀RPA魔法指令3.0驅動批量去水印,實現秒級素材凈化

本文 去除水印實操視頻展示電商圖片水印處理的困境?影刀 RPA 魔法指令 3.0 強勢登場?利用魔法指令3.0兩步實現去除水印操作關于影刀RPA 去除水印實操視頻展示 我們這里選擇了4張小紅書里面比較帥氣的圖片,但凡用過小紅書的都知道,小紅書右下角是會有小…

Seq2Seq - GRU補充講解

nn.GRU 是 PyTorch 中實現門控循環單元(Gated Recurrent Unit, GRU)的模塊。GRU 是一種循環神經網絡(RNN)的變體,用于處理序列數據,能夠更好地捕捉長距離依賴關系。 ?重點掌握輸入輸出部分輸入張量&#…

設計模式-觀察者模式和發布訂閱模式區別

文章目錄 其他不錯的文章 二者有類似的地方,也有區別。 引用的文章說的已經比較清楚了,這里只列出對比圖。 對比點觀察者模式發布訂閱模式中間人角色無事件中心,觀察者直接訂閱目標有事件中心,發布者與訂閱者通過事件中心通信關系…

【SQL】基于多源SQL 去重方法對比 -- 精華版

【SQL】基于SQL 去重方法對比 -- 精華版 一、引言二、基于SQL去重方法完整對比1. MySQL去重方法及優劣勢1.1 ?DISTINCT關鍵字1.2 GROUP BY子句1.3 UNION系列操作1.4 子查詢 自關聯 2. Hive去重方法及優劣勢2.1 DISTINCT關鍵字2.2 ?GROUP BY子句2.3 ?ROW_NUMBER窗口函數2.4 …

電腦命名配置很高,為什么運行軟件特別卡

估計很多同學都碰見過這種情況,以我的Redmi G為例,I9-14待CPU,又換了一條內存條,現有配置I9-14900,40G內存5200MT/s,4060顯卡,為啥運行兩個辦公軟件就卡的不行,風扇狂轉,…

Spring Boot默認注冊的轉換器列表及其功能說明。這些轉換器使得控制器方法可以直接接收Integer、Long、Date等類型參數,無需手動實現轉換

以下是Spring Boot默認注冊的轉換器列表及其功能說明。這些轉換器使得控制器方法可以直接接收Integer、Long、Date等類型參數,無需手動實現轉換: 默認轉換器列表及功能 1. 基礎類型轉換器 轉換器名稱功能示例場景StringToIntegerConverter將字符串轉換…

chrome提示https不安全, 不能記住賬號密碼怎么辦? 可以利用js輸入賬號

背景: 在內網搭建的服務, 由于https證書問題, 可能會被chrome瀏覽器提示不安全 此時, 默認的記住賬號密碼功能就無法使用, 那么此時只能手動輸入了嗎? 想到了幾種方案 1.利用外置軟件, 模擬按鍵輸入(比如按鍵精靈, 缺點是依賴外部軟件, 運行速度也慢, 且執行時占用了輸入焦…

探秘Transformer系列之(25)--- KV Cache優化之處理長文本序列

探秘Transformer系列之(25)— KV Cache優化之處理長文本序列 文章目錄 探秘Transformer系列之(25)--- KV Cache優化之處理長文本序列0x00 概述0x01 優化依據1.1 稀疏性1.2 重要性1.3 小結 0x02 稀疏化1.1 分類1.2 靜態稀疏化1.2.1…

【開發經驗】結合實際問題解決詳述HTTPS通信過程

最近的開發調試過程中涉及到了HTTPS發送與接收,遇到實際問題才發現對這部分尚屬于一知半解。結合實際問題的解決過程來詳細整理以下HTTPS通信過程。 需要調試的功能為BMC作為客戶端向搭建好的Web服務器發送HTTPS請求,Web服務器負責接收處理發送過來的HT…

【Android】Android Activity 橫屏設置詳解及常見異常問題解決方法匯總

在 Android 開發中,我們經常需要控制 Activity 的屏幕方向,例如視頻播放、游戲、VR/AR 應用等場景通常希望默認橫屏顯示。本文將講解如何通過 Manifest 配置 和 Java/Kotlin 代碼 設置橫屏顯示,并分析常見設置無效的原因與解決方法。 一、通過…

文件相關:echo重定向管道命令擴展詳解

一、echo 文字內容 echo 會在終端中顯示參數指定的文字,通常會和 重定向 聯合使用 二、重定向 > 和 >> Linux 允許將命令執行結果 重定向到一個 文件將本應顯示在終端上的內容 輸出 / 追加 到指定文件中 其中: >表示輸出,會覆…

Python 中使用單例模式

有這么一種場景,Web服務中有一個全局資源池,在需要使用的地方就自然而言引用該全局資源池即可,此時可以將該資源池以單例模式實現。隨后,需要為某一特殊業務場景專門準備一個全局資源池,于是額外復制一份代碼新建了一個…

websocket深入-webflux+websocket

文章目錄 背景版本約定配置文件代碼使用webflux使用websocket配置文件handler基類實現類注冊路由 背景 基于更復雜的情況和更高的開發要求,我們可能會遇到必須同時要使用webflux和websocket的情況。 版本約定 JDK21Springboot 3.2.0Fastjson2lombok 配置文件 &…

致遠OA —— 表單數據獲取(前端)

文章目錄 :apple: 業務需求描述 🍎 業務需求描述 測試案例: https://pan.quark.cn/s/3f58972f0a27 官網地址: 需求描述: 點擊獲取數據接口,調用后臺,將從后臺查詢到的數據回寫到表單的內容中。 如下…

51c嵌入式~繼電器~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/13775821 一、繼電器應用細節 繼電器的應用,相信大家都知道,在電路中只要給它供電、斷電也就可以工作了。本文討論它的應用細節。 現在流行的接法 圖中,繼電器的線圈經過Q1作為開關&am…

前端性能優化核彈級方案:CSS分層渲染+Wasm,首屏提速300%!

前端性能優化核彈級方案:CSS分層渲染Wasm實現首屏提速300%的終極指南 在當今Web應用日益復雜的背景下,性能優化已成為前端開發的核心競爭力。本文將深入剖析兩種革命性的前端性能優化技術——CSS分層渲染與WebAssembly(Wasm)的協同應用,揭示…

初識Redis · 簡單理解Redis

目錄 前言: 分布式系統 開源節流 認識Redis 負載均衡 緩存 微服務 前言: 本文只是作為Redis的一篇雜談,簡單理解一下Redis為什么要存在,以及它能做到和它不能做到的事兒,簡單提及一下它對應的優勢有什么&#…