偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

聊聊ReentrantLock 中的四個(gè)坑!

開(kāi)發(fā) 前端
本文介紹了 Java 中的顯式鎖 Lock 及其子類(lèi) ReentrantLock 的使用和注意事項(xiàng),Lock 在 Java 中占據(jù)了鎖的半壁江山,但在使用時(shí)卻要注意四個(gè)問(wèn)題。

作者 | 王磊

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

轉(zhuǎn)載請(qǐng)聯(lián)系授權(quán)(微信ID:GG_Stone)

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

本文咱們重點(diǎn)來(lái)看 Lock。

Lock 簡(jiǎn)介

Lock 是一個(gè)頂級(jí)接口,它的所有方法如下圖所示:

它的子類(lèi)列表如下:

 

我們通常會(huì)使用 ReentrantLock 來(lái)定義其實(shí)例,它們之間的關(guān)聯(lián)如下圖所示:

“PS:Sync 是同步鎖的意思,F(xiàn)airSync 是公平鎖,NonfairSync 是非公平鎖。

ReentrantLock 使用

學(xué)習(xí)任何一項(xiàng)技能都是先從使用開(kāi)始的,所以我們也不例外,咱們先來(lái)看下 ReentrantLock 的基礎(chǔ)使用:

  1. publicclass LockExample { 
  2.     // 創(chuàng)建鎖對(duì)象 
  3.     privatefinal ReentrantLock lock = new ReentrantLock(); 
  4.     public void method() { 
  5.         // 加鎖操作 
  6.         lock.lock(); 
  7.         try { 
  8.             // 業(yè)務(wù)代碼...... 
  9.         } finally { 
  10.             // 釋放鎖 
  11.             lock.unlock(); 
  12.         } 
  13.     } 

ReentrantLock 在創(chuàng)建之后,有兩個(gè)關(guān)鍵性的操作:

  • 加鎖操作:lock()
  • 釋放鎖操作:unlock()

ReentrantLock 中的坑

1.ReentrantLock 默認(rèn)為非公平鎖

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

  1. import java.util.concurrent.locks.ReentrantLock; 
  2.  
  3. publicclass LockExample { 
  4.     // 創(chuàng)建鎖對(duì)象 
  5.     privatestaticfinal ReentrantLock lock = new ReentrantLock(); 
  6.  
  7.     public static void main(String[] args) { 
  8.         // 定義線程任務(wù) 
  9.         Runnable runnable = new Runnable() { 
  10.             @Override 
  11.             public void run() { 
  12.                 // 加鎖 
  13.                 lock.lock(); 
  14.                 try { 
  15.                     // 打印執(zhí)行線程的名字 
  16.                     System.out.println("線程:" + Thread.currentThread().getName()); 
  17.                 } finally { 
  18.                     // 釋放鎖 
  19.                     lock.unlock(); 
  20.                 } 
  21.             } 
  22.         }; 
  23.         // 創(chuàng)建多個(gè)線程 
  24.         for (int i = 0; i < 10; i++) { 
  25.             new Thread(runnable).start(); 
  26.         } 
  27.     } 

以上程序的執(zhí)行結(jié)果如下:

從上述執(zhí)行的結(jié)果可以看出,ReentrantLock 默認(rèn)情況下為非公平鎖。因?yàn)榫€程的名稱(chēng)是根據(jù)創(chuàng)建的先后順序遞增的,所以如果是公平鎖,那么線程的執(zhí)行應(yīng)該是有序遞增的,但從上述的結(jié)果可以看出,線程的執(zhí)行和打印是無(wú)序的,這說(shuō)明 ReentrantLock 默認(rèn)情況下為非公平鎖。

想要將 ReentrantLock 設(shè)置為公平鎖也很簡(jiǎn)單,只需要在創(chuàng)建 ReentrantLock 時(shí),設(shè)置一個(gè) true 的構(gòu)造參數(shù)就可以了,如下代碼所示:

  1. import java.util.concurrent.locks.ReentrantLock; 
  2.  
  3. publicclass LockExample { 
  4.     // 創(chuàng)建鎖對(duì)象(公平鎖) 
  5.     privatestaticfinal ReentrantLock lock = new ReentrantLock(true); 
  6.  
  7.     public static void main(String[] args) { 
  8.         // 定義線程任務(wù) 
  9.         Runnable runnable = new Runnable() { 
  10.             @Override 
  11.             public void run() { 
  12.                 // 加鎖 
  13.                 lock.lock(); 
  14.                 try { 
  15.                     // 打印執(zhí)行線程的名字 
  16.                     System.out.println("線程:" + Thread.currentThread().getName()); 
  17.                 } finally { 
  18.                     // 釋放鎖 
  19.                     lock.unlock(); 
  20.                 } 
  21.             } 
  22.         }; 
  23.         // 創(chuàng)建多個(gè)線程 
  24.         for (int i = 0; i < 10; i++) { 
  25.             new Thread(runnable).start(); 
  26.         } 
  27.     } 

以上程序的執(zhí)行結(jié)果如下:

從上述結(jié)果可以看出,當(dāng)我們顯式的給 ReentrantLock 設(shè)置了 true 的構(gòu)造參數(shù)之后,ReentrantLock 就變成了公平鎖,線程獲取鎖的順序也變成有序的了。

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

  1. public ReentrantLock() { 
  2.      sync = new NonfairSync(); 
  3.  } 
  4. public ReentrantLock(boolean fair) { 
  5.     sync = fair ? new FairSync() : new NonfairSync(); 

從上述源碼中可以看出,默認(rèn)情況下 ReentrantLock 會(huì)創(chuàng)建一個(gè)非公平鎖,如果在創(chuàng)建時(shí)顯式的設(shè)置構(gòu)造參數(shù)的值為 true 時(shí),它就會(huì)創(chuàng)建一個(gè)公平鎖。

2.在 finally 中釋放鎖

使用 ReentrantLock 時(shí)一定要記得釋放鎖,否則就會(huì)導(dǎo)致該鎖一直被占用,其他使用該鎖的線程則會(huì)永久的等待下去,所以我們?cè)谑褂? ReentrantLock 時(shí),一定要在 finally 中釋放鎖,這樣就可以保證鎖一定會(huì)被釋放。

反例

  1. import java.util.concurrent.locks.ReentrantLock; 
  2.  
  3. publicclass LockExample { 
  4.     // 創(chuàng)建鎖對(duì)象 
  5.     privatestaticfinal ReentrantLock lock = new ReentrantLock(); 
  6.     public static void main(String[] args) { 
  7.         // 加鎖操作 
  8.         lock.lock(); 
  9.         System.out.println("Hello,ReentrantLock."); 
  10.         // 此處會(huì)報(bào)異常,導(dǎo)致鎖不能正常釋放 
  11.         int number = 1 / 0; 
  12.         // 釋放鎖 
  13.         lock.unlock(); 
  14.         System.out.println("鎖釋放成功!"); 
  15.     } 

以上程序的執(zhí)行結(jié)果如下:

從上述結(jié)果可以看出,當(dāng)出現(xiàn)異常時(shí)鎖未被正常釋放,這樣就會(huì)導(dǎo)致其他使用該鎖的線程永久的處于等待狀態(tài)。

正例

  1. import java.util.concurrent.locks.ReentrantLock; 
  2.  
  3. publicclass LockExample { 
  4.     // 創(chuàng)建鎖對(duì)象 
  5.     privatestaticfinal ReentrantLock lock = new ReentrantLock(); 
  6.     public static void main(String[] args) { 
  7.         // 加鎖操作 
  8.         lock.lock(); 
  9.         try { 
  10.             System.out.println("Hello,ReentrantLock."); 
  11.             // 此處會(huì)報(bào)異常 
  12.             int number = 1 / 0; 
  13.         } finally { 
  14.             // 釋放鎖 
  15.             lock.unlock(); 
  16.             System.out.println("鎖釋放成功!"); 
  17.         } 
  18.     } 

以上程序的執(zhí)行結(jié)果如下:

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

3.鎖不能被釋放多次

lock 操作的次數(shù)和 unlock 操作的次數(shù)必須一一對(duì)應(yīng),且不能出現(xiàn)一個(gè)鎖被釋放多次的情況,因?yàn)檫@樣就會(huì)導(dǎo)致程序報(bào)錯(cuò)。

反例

一次 lock 對(duì)應(yīng)了兩次 unlock 操作,導(dǎo)致程序報(bào)錯(cuò)并終止執(zhí)行,示例代碼如下:

  1. import java.util.concurrent.locks.ReentrantLock; 
  2.  
  3. publicclass LockExample { 
  4.     // 創(chuàng)建鎖對(duì)象 
  5.     privatestaticfinal ReentrantLock lock = new ReentrantLock(); 
  6.  
  7.     public static void main(String[] args) { 
  8.         // 加鎖操作 
  9.         lock.lock(); 
  10.          
  11.         // 第一次釋放鎖 
  12.         try { 
  13.             System.out.println("執(zhí)行業(yè)務(wù) 1~"); 
  14.             // 業(yè)務(wù)代碼 1...... 
  15.         } finally { 
  16.             // 釋放鎖 
  17.             lock.unlock(); 
  18.             System.out.println("鎖釋鎖"); 
  19.         } 
  20.  
  21.         // 第二次釋放鎖 
  22.         try { 
  23.             System.out.println("執(zhí)行業(yè)務(wù) 2~"); 
  24.             // 業(yè)務(wù)代碼 2...... 
  25.         } finally { 
  26.             // 釋放鎖 
  27.             lock.unlock(); 
  28.             System.out.println("鎖釋鎖"); 
  29.         } 
  30.         // 最后的打印操作 
  31.         System.out.println("程序執(zhí)行完成."); 
  32.     } 

以上程序的執(zhí)行結(jié)果如下:

從上述結(jié)果可以看出,執(zhí)行第 2 個(gè) unlock 時(shí),程序報(bào)錯(cuò)并終止執(zhí)行了,導(dǎo)致異常之后的代碼都未正常執(zhí)行。

4.lock 不要放在 try 代碼內(nèi)

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

反例

  1. import java.util.concurrent.locks.ReentrantLock; 
  2.  
  3. publicclass LockExample { 
  4.     // 創(chuàng)建鎖對(duì)象 
  5.     privatestaticfinal ReentrantLock lock = new ReentrantLock(); 
  6.  
  7.     public static void main(String[] args) { 
  8.         try { 
  9.             // 此處異常 
  10.             int num = 1 / 0; 
  11.             // 加鎖操作 
  12.             lock.lock(); 
  13.         } finally { 
  14.             // 釋放鎖 
  15.             lock.unlock(); 
  16.             System.out.println("鎖釋鎖"); 
  17.         } 
  18.         System.out.println("程序執(zhí)行完成."); 
  19.     } 

以上程序的執(zhí)行結(jié)果如下:

從上述結(jié)果可以看出,如果將加鎖操作放在 try 代碼中,可能會(huì)導(dǎo)致兩個(gè)問(wèn)題:

  1. 未加鎖成功就執(zhí)行了釋放鎖的操作,從而導(dǎo)致了新的異常;
  2. 釋放鎖的異常會(huì)覆蓋程序原有的異常,從而增加了排查問(wèn)題的難度。

總結(jié)

本文介紹了 Java 中的顯式鎖 Lock 及其子類(lèi) ReentrantLock 的使用和注意事項(xiàng),Lock 在 Java 中占據(jù)了鎖的半壁江山,但在使用時(shí)卻要注意 4 個(gè)問(wèn)題:

  1. 默認(rèn)情況下 ReentrantLock 為非公平鎖而非公平鎖;
  2. 加鎖次數(shù)和釋放鎖次數(shù)一定要保持一致,否則會(huì)導(dǎo)致線程阻塞或程序異常;
  3. 加鎖操作一定要放在 try 代碼之前,這樣可以避免未加鎖成功又釋放鎖的異常;
  4. 釋放鎖一定要放在 finally 中,否則會(huì)導(dǎo)致線程阻塞。

 

責(zé)任編輯:姜華 來(lái)源: Java中文社群
相關(guān)推薦

2021-11-05 07:59:25

HashMapJava知識(shí)總結(jié)

2022-01-12 15:50:24

JavaScript開(kāi)發(fā)循環(huán)

2022-12-19 16:07:22

數(shù)據(jù)治理IT

2022-12-12 08:47:06

2022-05-04 12:44:57

Python編程語(yǔ)言

2020-08-13 10:29:55

項(xiàng)目管理項(xiàng)目經(jīng)理CIO

2024-04-11 09:38:15

2022-03-02 08:20:54

并發(fā)編程java后端開(kāi)發(fā)

2011-07-14 15:23:34

java

2021-01-25 10:40:56

Python 開(kāi)發(fā)編程語(yǔ)言

2024-05-10 12:33:06

flask裝飾器

2024-06-25 12:45:05

2013-03-18 13:31:28

2022-02-23 15:09:18

數(shù)字化轉(zhuǎn)型國(guó)有企業(yè)數(shù)據(jù)

2022-03-09 09:43:20

并發(fā)編程Java

2023-10-26 07:47:35

JavaScript代碼變量

2010-03-30 11:00:46

Oracle 數(shù)據(jù)

2023-08-21 13:39:57

開(kāi)發(fā)桌面Ubuntu

2011-05-18 09:32:14

java

2024-11-14 09:00:00

Python編程元編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)