關於synchronized字段,不管該關鍵字是修飾方法還是修飾同步代碼塊,synchronzed拿到的都是對象。
當synchronized修飾的是方法時,synchronized所拿到的是調用該方法的對象的鎖,一般情況下都是this的鎖;
當synchronized()修飾的同步代碼塊時,synchronized拿到的是指定對象的鎖。
擴展:
當synchronized修飾的靜態方法時,由於靜態方法不包含this,屬於類層次的方法,所以,synchronized拿到的是這個方法所屬Class對象的鎖。
java中的對象每個只含有一個鎖,通過synchronized來獲取對象的鎖。
注意:當方法異常退出時,其對象鎖可以有JVM進行釋放。
占有鎖的線程釋放鎖時一般是以下三種情況:
1、 占有鎖的線程執行完了代碼塊,然后釋放對鎖的占有;
2、 占有鎖的線程發生了異常,此時JVM會讓線程自動釋放鎖;
3、 占有鎖的線程調用了wait()方法,從而進入了WAITING狀態需要釋放鎖。
探究-->對象都含有什么鎖?
對象鎖:又稱獨占排它鎖,通過名字我們便可知道,在多線程程序中,一旦一個線程到達了共享區(即synchronized修飾的區域)。那么該線程將擁有該共享區的對象鎖,其他線程想要進入,只能等到該線程釋放了鎖才可進入。對應於非靜態方法和非靜態代碼塊。
類鎖:方法或代碼塊所在類的類對象的鎖。對應於靜態方法和靜態代碼塊。
Lock接口
體系類圖:

方法介紹:
Lock:是Lock接口中使用最多的獲取鎖的方法,如果鎖被其他線程占用就等待。
由於Lock接口是基於JDK層面的,所以,鎖的釋放動作必須手動進行。不像synchronized是基於Java語言的特性,屬於JVM層面,鎖的獲取和釋放動作都由JVM自動進行,對開發者是透明的。
使用方式:
Lock lock = ...; lock.lock();
try{
//處理任務
}catch(Exception ex){
}finally{
}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{
}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的選擇:
1、 Lock是一個接口,屬於JDK層面的實現;而synchronized屬於Java語言的特性,其實現有JVM來控制。
2、 synchronized在發生異常時,會自動釋放掉鎖,故不會發生死鎖現(此時的死鎖一般是代碼邏輯引起的);而Lock必須在finally中主動unlock鎖,否則就會出現死鎖。
3、 Lock能夠響應中斷,讓等待狀態的線程停止等待;而synchronized不行。
4、 通過Lock可以知道線程是否成功獲得了鎖,而synchronized不行。
5、 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。