1. 程式人生 > >ReentrantLock(一):談談ReentrantLock與synchronized

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 “效能會更好”。請記住,這些是供高階使用者使用的高階工具。(而且,真正的高階使用者喜歡選擇能夠找到的最簡單工具,直到他們認為簡單的工具不適用為止。)。一如既往,首先要把事情做好,然後再考慮是不是有必要做得更快。