關於synchronize與lock的區別


參考文獻:https://www.cnblogs.com/cloudblogs/p/6440160.html

一、synchronize修飾不同代碼都是鎖住了什么?
大家都知道synchronize可以修飾屬性、代碼塊,方法、類,但是修飾不同的代碼鎖住的內容是不同的。
1、修飾 非靜態屬性和方法時,拿到的是調用這個方法或者屬性的 對象(this)的鎖
2、synchronize()修飾代碼塊時,拿到的是 指定對象的鎖
3、 修飾類、靜態方法、靜態代碼塊時,由於沒有this指針,因此拿到的是 類鎖,也就是該類的class對象
!!!注意:一個對象只有一個鎖
 
二、關於synchronize
由於synchronize是由JVM實現的,因此當加鎖代碼出現異常時,對象鎖可以由JVM釋放,包含以下三種情況:
1、 占有鎖的線程執行完了代碼塊,然后釋放對鎖的占有;
2、 占有鎖的線程發生了異常,此時JVM會讓線程自動釋放鎖;
3、 占有鎖的線程調用了wait()方法,從而進入了WAITING狀態需要釋放鎖。
 
三、關於Lock

 

 由於Lock是由JDK實現的,所以不像synchronize鎖的獲取和釋放都是由JVM控制的,Lock的獲取和釋放都需要手動進行,並且在發生異常時,不會自動釋放鎖。因此一般來說,使用Lock必須在try{}catch{}塊中進行,並且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,防止死鎖的發生

1、lock() 獲取鎖與釋放鎖 ,如果鎖已被其他線程獲取,則進行等待。

1 Lock lock = ...; lock.lock();
2 try{    
3    //處理任務
4 }catch(Exception ex){
5 }finally{
6      lock.unlock();   //釋放鎖 
7 }

 

2、tryLock() 獲取鎖時有返回值可以得知獲取鎖操作是否成功 

 1 Lock lock = ...; 
 2 if(lock.tryLock()) {
 3       try{ 
 4          //處理任務     
 5    }catch(Exception ex){
 6      }finally{
 7           lock.unlock();   //釋放鎖      
 8   }  
 9 }else {   
10   //如果不能獲取鎖,則直接做其他事情 
11 }

如果獲取成功則返回True,如果鎖被其他線程占用則返回FALSE,該方法會立即返回結果不會讓線程一直處於等待狀態

 

3、tryLock(long time,TimeUnit unit) 與tryLock() 類似,但是與tryLock立即返回結果不同,該方法在拿不到鎖的情況下回等待time時間,如果在限定時間內還是拿不到鎖就返回FALSE,如果在一開始或者等待時間內拿到鎖則返回TRUE。

 

4、lockInterruptibly() 

通過這個方法嘗試獲取鎖時,如果線程正在等待獲取鎖,則該線程可以響應Thread.interrupt()中斷。synchronize對於沒有獲得鎖處於等待狀態的線程無法響應中斷。

1 public void method() throws InterruptedException {     
2     lock.lockInterruptibly();    
3     try {        //.....     }     
4     finally { lock.unlock(); }   
5 }

lockInterruptibly方法必須放在try塊中或者在調用lockInterruptibly的方法外聲明拋出InterruptedException,推薦使用后者。

 

5、readWriteLock()

該鎖提升了讀操作的效率,不過要注意的是,如果有一個線程已經占用了讀鎖,則此時其他線程如果要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖。如果有一個線程已經占用了寫鎖,則此時其他線程如果申請寫鎖或者讀鎖,則申請的線程也會一直等待釋放寫鎖。

 

四、Lock和synchronized的選擇:
1、 Lock是一個接口,屬於JDK層面的實現;而synchronized屬於Java語言的特性,其實現有JVM來控制(代碼執行完畢,出現異常,wait時JVM會主動釋放鎖)。
2、 synchronized在發生異常時,會自動釋放掉鎖,故不會發生死鎖現(此時的死鎖一般是代碼邏輯引起的);而Lock必須在finally中主動unlock鎖,否則就會出現死鎖。
3、 Lock能夠響應中斷,讓等待狀態的線程停止等待;而synchronized不行。
4、 通過Lock可以知道線程是否成功獲得了鎖,而synchronized不行。
5、 Lock提高了多線程下對讀操作的效率。
 
五、擴展
1、可重入鎖:synchronized和ReentrantLock都是可重入鎖。當一個線程執行到某個synchronized方法時,比如說method1,而在method1中會調用另外一個synchronized方法method2,此時線程不必重新去申請鎖,而是可以直接執行方法method2。
 
2、可中斷鎖:等待獲得鎖的等待過程是否可以中斷。通過上面的例子,我們可以得知Lock是可中斷鎖,而synchronized不是。
 
3、公平鎖:盡量以請求的順序來獲取鎖,同是有多個線程在等待一個鎖,當這個鎖被釋放時,等待時間最久的線程(最先請求的線程)會獲得該鎖。synchronized是非公平鎖,而ReentrantLock和ReentReadWriteLock默認情況下是非公平鎖,但是可以設置成公平鎖。
ReentrantLock lock = new ReentrantLock(true);
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
設置為TRUE即為公平鎖,為FALSE或者無參數為非公平鎖。
 
4、讀寫鎖:讀寫鎖將對臨界資源的訪問分成了兩個鎖,一個讀鎖和一個寫鎖。增加讀寫靈活性。即ReadWriteLock接口及其實現ReentrantReadWriteLock。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM