日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ReentrantLock 中的 4 个坑!

發布時間:2025/3/11 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ReentrantLock 中的 4 个坑! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | 王磊

來源 | Java中文社群(ID:javacn666)

轉載請聯系授權(微信ID:GG_Stone)

JDK 1.5 之前 synchronized 的性能是比較低的,但在 JDK 1.5 中,官方推出一個重量級功能 Lock,一舉改變了 Java 中鎖的格局。JDK 1.5 之前當我們談到鎖時,只能使用內置鎖 synchronized,但如今我們鎖的實現又多了一種顯式鎖 Lock。

前面的文章我們已經介紹了 synchronized,詳見以下列表:

《synchronized 加鎖 this 和 class 的區別!》

《synchronized 優化手段之鎖膨脹機制!》

《synchronized 中的 4 個優化,你知道幾個?》

所以本文咱們重點來看 Lock。

Lock 簡介

Lock 是一個頂級接口,它的所有方法如下圖所示:

它的子類列表如下:

我們通常會使用 ReentrantLock 來定義其實例,它們之間的關聯如下圖所示:

PS:Sync 是同步鎖的意思,FairSync 是公平鎖,NonfairSync 是非公平鎖。

ReentrantLock 使用

學習任何一項技能都是先從使用開始的,所以我們也不例外,咱們先來看下 ReentrantLock 的基礎使用:

public class LockExample {// 創建鎖對象private final ReentrantLock lock = new ReentrantLock();public void method() {// 加鎖操作lock.lock();try {// 業務代碼......} finally {// 釋放鎖lock.unlock();}} }

ReentrantLock 在創建之后,有兩個關鍵性的操作:

  • 加鎖操作:lock()

  • 釋放鎖操作:unlock()

ReentrantLock 中的坑

1.ReentrantLock 默認為非公平鎖

很多人會認為(尤其是新手朋友),ReentrantLock 默認的實現是公平鎖,其實并非如此,ReentrantLock 默認情況下為非公平鎖(這主要是出于性能方面的考慮),比如下面這段代碼:

import java.util.concurrent.locks.ReentrantLock;public class LockExample {// 創建鎖對象private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {// 定義線程任務Runnable runnable = new Runnable() {@Overridepublic void run() {// 加鎖lock.lock();try {// 打印執行線程的名字System.out.println("線程:" + Thread.currentThread().getName());} finally {// 釋放鎖lock.unlock();}}};// 創建多個線程for (int i = 0; i < 10; i++) {new Thread(runnable).start();}} }

以上程序的執行結果如下:

從上述執行的結果可以看出,ReentrantLock 默認情況下為非公平鎖。因為線程的名稱是根據創建的先后順序遞增的,所以如果是公平鎖,那么線程的執行應該是有序遞增的,但從上述的結果可以看出,線程的執行和打印是無序的,這說明 ReentrantLock 默認情況下為非公平鎖。

想要將 ReentrantLock 設置為公平鎖也很簡單,只需要在創建 ReentrantLock 時,設置一個 true 的構造參數就可以了,如下代碼所示:

import java.util.concurrent.locks.ReentrantLock;public class LockExample {// 創建鎖對象(公平鎖)private static final ReentrantLock lock = new ReentrantLock(true);public static void main(String[] args) {// 定義線程任務Runnable runnable = new Runnable() {@Overridepublic void run() {// 加鎖lock.lock();try {// 打印執行線程的名字System.out.println("線程:" + Thread.currentThread().getName());} finally {// 釋放鎖lock.unlock();}}};// 創建多個線程for (int i = 0; i < 10; i++) {new Thread(runnable).start();}} }

以上程序的執行結果如下:

從上述結果可以看出,當我們顯式的給 ReentrantLock 設置了 true 的構造參數之后,ReentrantLock 就變成了公平鎖,線程獲取鎖的順序也變成有序的了。

其實從 ReentrantLock 的源碼我們也可以看出它究竟是公平鎖還是非公平鎖,ReentrantLock 部分源碼實現如下:

public ReentrantLock() {sync = new NonfairSync();} public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync(); }

從上述源碼中可以看出,默認情況下 ReentrantLock 會創建一個非公平鎖,如果在創建時顯式的設置構造參數的值為 true 時,它就會創建一個公平鎖。

2.在 finally 中釋放鎖

使用 ReentrantLock 時一定要記得釋放鎖,否則就會導致該鎖一直被占用,其他使用該鎖的線程則會永久的等待下去,所以我們在使用 ReentrantLock 時,一定要在 finally 中釋放鎖,這樣就可以保證鎖一定會被釋放。

反例

import java.util.concurrent.locks.ReentrantLock;public class LockExample {// 創建鎖對象private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {// 加鎖操作lock.lock();System.out.println("Hello,ReentrantLock.");// 此處會報異常,導致鎖不能正常釋放int number = 1 / 0;// 釋放鎖lock.unlock();System.out.println("鎖釋放成功!");} }

以上程序的執行結果如下:

從上述結果可以看出,當出現異常時鎖未被正常釋放,這樣就會導致其他使用該鎖的線程永久的處于等待狀態。

正例

import java.util.concurrent.locks.ReentrantLock;public class LockExample {// 創建鎖對象private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {// 加鎖操作lock.lock();try {System.out.println("Hello,ReentrantLock.");// 此處會報異常int number = 1 / 0;} finally {// 釋放鎖lock.unlock();System.out.println("鎖釋放成功!");}} }

以上程序的執行結果如下:

從上述結果可以看出,雖然方法中出現了異常情況,但并不影響 ReentrantLock 鎖的釋放操作,這樣其他使用此鎖的線程就可以正常獲取并運行了。

3.鎖不能被釋放多次

lock 操作的次數和 unlock 操作的次數必須一一對應,且不能出現一個鎖被釋放多次的情況,因為這樣就會導致程序報錯。

反例

一次 lock 對應了兩次 unlock 操作,導致程序報錯并終止執行,示例代碼如下:

import java.util.concurrent.locks.ReentrantLock;public class LockExample {// 創建鎖對象private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {// 加鎖操作lock.lock();// 第一次釋放鎖try {System.out.println("執行業務 1~");// 業務代碼 1......} finally {// 釋放鎖lock.unlock();System.out.println("鎖釋鎖");}// 第二次釋放鎖try {System.out.println("執行業務 2~");// 業務代碼 2......} finally {// 釋放鎖lock.unlock();System.out.println("鎖釋鎖");}// 最后的打印操作System.out.println("程序執行完成.");} }

以上程序的執行結果如下:

從上述結果可以看出,執行第 2 個 unlock 時,程序報錯并終止執行了,導致異常之后的代碼都未正常執行。

4.lock 不要放在 try 代碼內

在使用 ReentrantLock 時,需要注意不要將加鎖操作放在 try 代碼中,這樣會導致未加鎖成功就執行了釋放鎖的操作,從而導致程序執行異常。

反例

import java.util.concurrent.locks.ReentrantLock;public class LockExample {// 創建鎖對象private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {try {// 此處異常int num = 1 / 0;// 加鎖操作lock.lock();} finally {// 釋放鎖lock.unlock();System.out.println("鎖釋鎖");}System.out.println("程序執行完成.");} }

以上程序的執行結果如下:

從上述結果可以看出,如果將加鎖操作放在 try 代碼中,可能會導致兩個問題:

  • 未加鎖成功就執行了釋放鎖的操作,從而導致了新的異常;

  • 釋放鎖的異常會覆蓋程序原有的異常,從而增加了排查問題的難度。

  • 總結

    本文介紹了 Java 中的顯式鎖 Lock 及其子類 ReentrantLock 的使用和注意事項,Lock 在 Java 中占據了鎖的半壁江山,但在使用時卻要注意 4 個問題:

  • 默認情況下 ReentrantLock 為非公平鎖而非公平鎖;

  • 加鎖次數和釋放鎖次數一定要保持一致,否則會導致線程阻塞或程序異常;

  • 加鎖操作一定要放在 try 代碼之前,這樣可以避免未加鎖成功又釋放鎖的異常;

  • 釋放鎖一定要放在 finally 中,否則會導致線程阻塞。

  • 文末福利

    今天恰好情人節磊哥聯合博文視點出版社,給大家送 3 本何海濤老師的經典書籍《劍指Offer(專項突破版):數據結構與算法名企面試題精講》,作者從微軟起步,面遍各國際大公司,且有近 20 年名企面試官經歷,擔任面試官面試千余次,積累大量真實試題和現場經驗。

    中獎規則:評論區留的第 6、16、26 位用戶送出此書,免費包郵到家,下周二開獎。

    當然,土豪朋友也可以通過下面連接直接購買。

    本系列原創文章推薦

    1.線程的故事:我的3位母親成就了優秀的我!

    2.線程池的7種創建方式,強烈推薦你用它...

    3.輕量級鎖一定比重量級鎖快嗎?

    4.這樣終止線程,竟然會導致服務宕機?

    5.漫畫:如何證明sleep不釋放鎖,而wait釋放鎖?

    6.池化技術到達有多牛?看了這個對比嚇我一跳!

    7.求求你,別再用wait和notify了!

    8.Semaphore自白:限流器用我就對了!

    9.CountDownLatch:別浪,等人齊再團!

    10.CyclicBarrier:人齊了,老司機就發車了!

    11.Java中用戶線程和守護線程區別這么大?

    12.ThreadLocal不好用?那是你沒用對!

    13.ThreadLocal內存溢出代碼演示和原因分析!

    14.SimpleDateFormat線程不安全的5種解決方案!

    15.synchronized 加鎖 this 和 class 的區別!

    16.synchronized 優化手段之鎖膨脹機制!

    17.synchronized 中的 4 個優化,你知道幾個?

    總結

    以上是生活随笔為你收集整理的ReentrantLock 中的 4 个坑!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。