Synchronized和Lock接口

關於synchronized字段,無論該關鍵字是修飾方法仍是修飾同步代碼塊,synchronzed拿到的都是對象。
當synchronized修飾的是方法時,synchronized所拿到的是調用該方法的對象的鎖,通常狀況下都是this的鎖;
當synchronized()修飾的同步代碼塊時,synchronized拿到的是指定對象的鎖。
 
擴展:
當synchronized修飾的靜態方法時,因爲靜態方法不包含this,屬於類層次的方法,因此,synchronized拿到的是這個方法所屬Class對象的鎖。
java中的對象每一個只含有一個鎖,經過synchronized來獲取對象的鎖。
 
注意:當方法異常退出時,其對象鎖能夠有JVM進行釋放。
佔有鎖的線程釋放鎖時通常是如下三種狀況:
一、 佔有鎖的線程執行完了代碼塊,而後釋放對鎖的佔有;
二、 佔有鎖的線程發生了異常,此時JVM會讓線程自動釋放鎖;
三、 佔有鎖的線程調用了wait()方法,從而進入了WAITING狀態須要釋放鎖。
 
探究-->對象都含有什麼鎖?
對象鎖:又稱獨佔排它鎖,經過名字咱們即可知道,在多線程程序中,一旦一個線程到達了共享區(即synchronized修飾的區域)。那麼該線程將擁有該共享區的對象鎖,其餘線程想要進入,只能等到該線程釋放了鎖纔可進入。對應於非靜態方法和非靜態代碼塊。
類鎖:方法或代碼塊所在類的類對象的鎖。對應於靜態方法和靜態代碼塊。
 
 
Lock接口
體系類圖:
方法介紹:
Lock:是Lock接口中使用最多的獲取鎖的方法,若是鎖被其餘線程佔用就等待。 因爲Lock接口是基於JDK層面的,因此,鎖的釋放動做必須手動進行。不像synchronized是基於Java語言的特性,屬於JVM層面,鎖的獲取和釋放動做都由JVM自動進行,對開發者是透明的。
使用方式:
Lock lock = ...; lock.lock();
try{    
   //處理任務
}catch(Exception ex){
}finally{
    lock.unlock();   //釋放鎖
}
 
tryLock:表示用來嘗試獲取鎖,若是獲取成功即返回TRUE,若是鎖被其餘線程佔用,則返回FALSE;該方法會當即返回結果,不會一直處於等待狀態。
 
tryLock(long time,TimeUnit unit):與tryLock相似,區別在於這個方法在拿不到鎖的狀況下會等待一個時間time, 在時間期限以內若是還拿不到鎖,就返回FALSE;同時能夠相應中斷。若是在一開始或者等待期間得到了鎖,則返回true。
問題:若是在開始沒有拿到鎖,那麼代碼塊會進入到等待狀態麼?
猜測:我以爲會進入等待狀態,不過在等待的這段時間內,會存在一個響應機制來監測別的線程是否釋放了鎖,若是釋放了鎖,則直接有該線程獲取鎖,該方法返回TRUE;若是沒有釋放,那麼該方法將再也不享有響應機制的提醒,並返回FALSE。
使用方式:
Lock lock = ...;
if(lock.tryLock()) {
     try{
         //處理任務     
   }catch(Exception ex){
     }finally{
         lock.unlock();   //釋放鎖     
  }
}else {   
  //若是不能獲取鎖,則直接作其餘事情
}
lockInterruptibly:當經過這個方法嘗試獲取鎖時,若是線程正在等待獲取鎖,那麼這個線程可以響應中斷,即被本身或者其餘線程中斷線程的等待狀態。例如,當兩個線程同時經過lock.lockInterruptibly()方法獲取鎖時,假如線程A獲取了鎖,那麼線程B就只能進入等待狀態,那麼對線程B調用ThreadB.interrupt()可以中斷線程B的等待狀態。
 
注意:lockInterruptibly方法必須放在try塊中或者在調用lockInterruptibly的方法外聲明拋出InterruptedException,推薦使用後者。
 
使用方式:
public void method() throws InterruptedException {     lock.lockInterruptibly();     try {       //.....     }     finally {         lock.unlock();     }  }
疑問:釋放掉線程的等待狀態有什麼用處?
相比於synchronized,等待狀態的線程沒法響應中斷。提升了代碼的靈活性,當不想持續的等待下去時,響應中斷去作其他的事情,更具靈活性。
 
ReadWriteLock
該鎖提高了讀操做的效率,不過要注意的是, 若是有一個線程已經佔用了讀鎖,則此時其餘線程若是要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖。若是有一個線程已經佔用了寫鎖,則此時其餘線程若是申請寫鎖或者讀鎖,則申請的線程也會一直等待釋放寫鎖。
 
 
Lock和synchronized的選擇:
一、 Lock是一個接口,屬於JDK層面的實現;而synchronized屬於Java語言的特性,其實現有JVM來控制。
二、 synchronized在發生異常時,會自動釋放掉鎖,故不會發生死鎖現(此時的死鎖通常是代碼邏輯引發的);而Lock必須在finally中主動unlock鎖,不然就會出現死鎖。
三、 Lock可以響應中斷,讓等待狀態的線程中止等待;而synchronized不行。
四、 經過Lock能夠知道線程是否成功得到了鎖,而synchronized不行。
五、 Lock提升了多線程下對讀操做的效率。
 
擴展
可重入鎖:synchronized和ReentrantLock都是可重入鎖。當一個線程執行到某個synchronized方法時,好比說method1,而在method1中會調用另一個synchronized方法method2,此時線程沒必要從新去申請鎖,而是能夠直接執行方法method2。
 
可中斷鎖:經過上面的例子,咱們能夠得知Lock是可中斷鎖,而synchronized不是。
 
公平鎖:儘可能以請求的順序來獲取鎖,同是有多個線程在等待一個鎖,當這個鎖被釋放時,等待時間最久的線程(最早請求的線程)會得到該鎖。synchronized是非公平鎖,而ReentrantLock和ReentReadWriteLock默認狀況下是非公平鎖,可是能夠設置成公平鎖
ReentrantLock lock = new ReentrantLock(true);
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
設置爲TRUE即爲公平鎖,爲FALSE或者無參數爲非公平鎖。
 
讀寫鎖:讀寫鎖將對臨界資源的訪問分紅了兩個鎖,一個讀鎖和一個寫鎖。即ReadWriteLock接口及其實現ReentrantReadWriteLock。
相關文章
相關標籤/搜索