並發編程中,鎖是經常需要用到的,今天我們一起來看下Java中的鎖機制:synchronized
和lock
。
synchronized
Synchronized
是Java 並發編程中很重要的關鍵字,另外一個很重要的是 volatile
。Syncronized
的目的是一次只允許一個線程進入由他修飾的代碼段,從而允許他們進行自我保護。
Synchronized
很像生活中的鎖例子,進入由Synchronized
保護的代碼區首先需要獲取 Synchronized 這把鎖,其他線程想要執行必須進行等待。Synchronized
鎖住的代碼區域執行完成后需要把鎖歸還,也就是釋放鎖,這樣才能夠讓其他線程使用。
它提供了⼀種獨占的加鎖⽅式。Synchronized
的獲取和釋放鎖由JVM
實現,⽤戶不需要顯示的釋放鎖,⾮常⽅便。然⽽synchronized
也有⼀定的局限性:
-
當線程嘗試獲取鎖的時候,如果獲取不到鎖會⼀直阻塞。屬於是一種悲觀鎖。
-
如果獲取鎖的線程進⼊休眠或者阻塞,除⾮當前線程異常,否則其他線程嘗試獲取鎖必須⼀直等待。
synchronized
可以鎖代碼塊、方法和對象。
- 方法聲明時使用,放在范圍操作符之后,返回類型聲明之前。即一次只能有一個線程進入該方法,其他線程要想在此時調用該方法,只能排隊等候。當作用於靜態方法時,鎖住的是Class實例,又因為Class的相關數據存儲在永久帶
PermGen
(jdk1.8 則是metaspace
),永久帶是全局共享的,因此靜態方法鎖相當於類的一個全局鎖,會鎖所有調用該方法的線程;
private int number;
public synchronized void numIncrease(){
number++;
}
- 你也可以在某個代碼塊上使用
Synchronized
關鍵字,表示只能有一個線程進入某個代碼段。
public void numDecrease(Object num){
synchronized (num){
number++;
}
}
synchronized
后面括號里是一對象,鎖住的是所有以該對象為鎖的代碼塊,此時線程獲得的是對象鎖。
public void test() {
synchronized (this) {
// ...
}
}
lock
Lock
是 Java並發編程中很重要的一個(Lock interface)接口,它要比 synchronized
關鍵字更能直譯"鎖"的概念,Lock需要手動加鎖和手動解鎖,一般通過 lock.lock()
方法來進行加鎖, 通過 lock.unlock()
方法進行解鎖。Lock
還有更強大的功能,例如,它的 tryLock
方法可以非阻塞方式去拿鎖。
Lock
接⼝⽐同步⽅法和同步塊提供了更具擴展性的鎖操作。他們允許更靈活的結構,可以具有完全不同的性質,並且可以⽀持多個相關類的條件對象。
它的優勢有:
- 可以使鎖更公平
- 可以使線程在等待鎖的時候響應中斷
- 可以讓線程嘗試獲取鎖,並在⽆法獲取鎖的時候⽴即返回或者等待⼀段時間,不會造成死鎖。
- 可以在不同的范圍,以不同的順序獲取和釋放鎖
與 Lock
關聯密切的鎖有 ReentrantLock
和 ReadWriteLock
。
ReetrantLock
實現了Lock接口,它是一個可重入鎖,內部定義了公平鎖與非公平鎖。
可重⼊鎖是指同⼀個線程可以多次獲取同⼀把鎖。ReentrantLock和synchronized都是可重⼊鎖。
可中斷鎖是指線程嘗試獲取鎖的過程中,是否可以響應中斷。
synchronized
是不可中斷鎖,⽽ReentrantLock
則提供了中斷功能。公平鎖是指多個線程同時嘗試獲取同⼀把鎖時,獲取鎖的順序按照線程達到的順序。
⾮公平鎖則允許線程“插隊”。
synchronized
是⾮公平鎖,⽽ReentrantLock
的默認實現是⾮公平鎖,但是也可以設置為公平鎖。
ReentrantLock
它是JDK 1.5之后提供的API層⾯的互斥鎖,需要lock()和unlock()⽅法配合try/finally語句塊來完成。等待可中斷避免,出現死鎖的情況(如果別的線程正持有鎖,會等待參數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false)
公平鎖與⾮公平鎖多個線程等待同⼀個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖⾮公平鎖,
ReentrantLock
默認的構造函數是創建的⾮公平鎖,可以通過參數true設為公平鎖,但公平鎖表現的性能不是很好。
ReadWriteLock
一個用來獲取讀鎖,一個用來獲取寫鎖。也就是說將文件的讀寫操作分開,分成2個鎖來分配給線程,從而使得多個線程可以同時進行讀操作。ReentrantReadWirteLock
實現了ReadWirteLock
接口,並未實現Lock
接口。
總結
synchronized
是Java內置的一個關鍵字,Lock
是是一個Java接口synchronized
無法判斷獲取鎖的狀態,而lock
鎖可以判斷是否獲取到了鎖synchronized
回自動釋放鎖,而lock
必須手動釋放鎖。如果不釋放就會變成死鎖synchronized
線程1(獲得鎖,阻塞)線程2(傻傻地等待),lock
就不一定會等待synchronized
可重入鎖,不可以中斷的,非公平。lock
鎖 可重入鎖,可以判斷鎖,公平不公平自己可以設置synchronized
適合鎖少量的代碼同步問題Lock
適合鎖大量的同步代碼