ReentrantLock(一):談談ReentrantLock與synchronized
該篇簡單介紹ReentrantLock 和synchronized的區別,介紹比較淺的ReentrantLock API應用
ReentrantLock 擁有Synchronized相同的併發性和記憶體語義,但是添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用情況下更佳的效能。(換句話說,當許多執行緒都想訪問共享資源時,JVM 可以花更少的時候來排程執行緒,把更多時間用在執行執行緒上。)
如下例子:
public class ReentrantLockTest { public final static ReentrantLock lock=new ReentrantLock(); public static void main(String[] args) { // TODO Auto-generated method stub for(int i =1;i<5;i++){ T t1=new T(i,lock); t1.start(); } } } class T extends Thread{ private int threadId; int i = 0; ReentrantLock lock = null; public T(int threadId,ReentrantLock lock){ this.threadId=threadId; this.lock = lock; } public void run(){ try{ lock.lock(); System.out.println("Thread ["+threadId+"]lock!"); System.out.println("執行緒鎖住了開始工作了"+threadId); System.out.println("Thread ["+threadId+"]"+lock.isHeldByCurrentThread()); }finally{ System.out.println("任務完成了執行緒解鎖了"+threadId); lock.unlock(); } }
從以上例子可以看出,ReentrantLock.lock()方法同樣具有“物件監視器”,其他執行緒只有等待鎖被釋放時再次爭搶,效果和使用synchronized關鍵字一樣,執行緒之間還是順序執行的。它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大。
方法摘要
getHoldCount
public int getHoldCount() 查詢當前執行緒保持此鎖的次數。 對於與解除鎖操作不匹配的每個鎖操作,執行緒都會保持一個鎖。 保持計數資訊通常只用於測試和除錯。 通俗點就是查詢當前執行緒保持鎖定的個數,也就是呼叫lock()方法的次數
getQueueLength
public final int getQueueLength()
返回正等待獲取此鎖的執行緒估計數。該值僅是估計的數字,因為在此方法遍歷內部資料結構的同時,執行緒的數目可能動態地變化。此方法用於監視系統狀態,不用於同步控制。
返回:
正在等待此鎖的執行緒估計數
比如有30個執行緒,1個執行緒首先執行了await()方法,那麼在呼叫getQueueLength()方法後返回值是29,說明有29個執行緒在等待lock的釋放
應用例子如下:
public class ReentrantLockTest { public final static ReentrantLock lock=new ReentrantLock(); public static void main(String[] args) { // TODO Auto-generated method stub for(int i =1;i<30;i++){ T t1=new T(i,lock); t1.start(); } System.out.println("有執行緒 = "+lock.getQueueLength()+"在等待獲取鎖"); } } class T extends Thread{ private int threadId; int i = 0; ReentrantLock lock = null; public T(int threadId,ReentrantLock lock){ this.threadId=threadId; this.lock = lock; } public void run(){ try{ lock.lock(); System.out.println("Thread ["+threadId+"]lock!"); System.out.println("執行緒鎖住了開始工作了"+threadId); System.out.println("lock.getHoldCount() = "+lock.getHoldCount()); System.out.println("Thread ["+threadId+"]"+lock.isHeldByCurrentThread()); methodA(); }finally{ System.out.println("任務完成了執行緒解鎖了"+threadId); lock.unlock(); } } public void methodA(){ try{ lock.lock(); System.out.println("再次獲取鎖"); }finally{ lock.unlock(); } }
控制檯輸出結果:
Thread [1]lock!
執行緒鎖住了開始工作了1
lock.getHoldCount() = 1
Thread [1]true
再次獲取鎖
任務完成了執行緒解鎖了1
Thread [5]lock!
執行緒鎖住了開始工作了5
lock.getHoldCount() = 1
Thread [5]true
再次獲取鎖
任務完成了執行緒解鎖了5
Thread [2]lock!
執行緒鎖住了開始工作了2
lock.getHoldCount() = 1
Thread [2]true
再次獲取鎖
任務完成了執行緒解鎖了2
Thread [9]lock!
執行緒鎖住了開始工作了9
lock.getHoldCount() = 1
Thread [9]true
再次獲取鎖
任務完成了執行緒解鎖了9
Thread [13]lock!
執行緒鎖住了開始工作了13
lock.getHoldCount() = 1
Thread [13]true
再次獲取鎖
任務完成了執行緒解鎖了13
Thread [3]lock!
執行緒鎖住了開始工作了3
lock.getHoldCount() = 1
Thread [3]true
再次獲取鎖
任務完成了執行緒解鎖了3
Thread [19]lock!
執行緒鎖住了開始工作了19
lock.getHoldCount() = 1
Thread [19]true
再次獲取鎖
任務完成了執行緒解鎖了19
Thread [4]lock!
執行緒鎖住了開始工作了4
lock.getHoldCount() = 1
Thread [4]true
再次獲取鎖
任務完成了執行緒解鎖了4
Thread [22]lock!
執行緒鎖住了開始工作了22
lock.getHoldCount() = 1
Thread [22]true
再次獲取鎖
任務完成了執行緒解鎖了22
Thread [25]lock!
執行緒鎖住了開始工作了25
lock.getHoldCount() = 1
有執行緒 = 16在等待獲取鎖
Thread [25]true
再次獲取鎖
任務完成了執行緒解鎖了25
Thread [27]lock!
執行緒鎖住了開始工作了27
什麼時候選擇用 ReentrantLock 代替 synchronized
既然如此,我們什麼時候才應該使用 ReentrantLock 呢?答案非常簡單 —— 在確實需要一些 synchronized 所沒有的特性的時候,比如時間鎖等候、可中斷鎖等候、無塊結構鎖、多個條件變數或者鎖投票。 ReentrantLock 還具有可伸縮性的好處,應當在高度爭用的情況下使用它,但是請記住,大多數 synchronized 塊幾乎從來沒有出現過爭用,所以可以把高度爭用放在一邊。我建議用 synchronized 開發,直到確實證明 synchronized 不合適,而不要僅僅是假設如果使用 ReentrantLock “效能會更好”。請記住,這些是供高階使用者使用的高階工具。(而且,真正的高階使用者喜歡選擇能夠找到的最簡單工具,直到他們認為簡單的工具不適用為止。)。一如既往,首先要把事情做好,然後再考慮是不是有必要做得更快。