synchronized和lock的使用分析(優缺點對比詳解)
1.synchronized
加同步格式:
synchronized(需要一個任意的物件(鎖)){
程式碼塊中放操作共享資料的程式碼。
}
synchromized缺陷
synchronized是java中的一個關鍵字,也就是說是java語言的內建的特性。
如果一個程式碼塊被synchronized修飾,當一個執行緒獲取了對應的鎖,並執行程式碼塊時,其他執行緒只能一直等待,等待獲取鎖的執行緒釋放鎖,而這裡獲取鎖的執行緒釋放鎖只會有兩種情況:
(1)獲取鎖的執行緒執行完了改程式碼塊,然後執行緒釋放對鎖的佔有
(2)執行緒執行發生異常,此時JVM會讓執行緒自動釋放鎖。
例子1:
如果這個獲取鎖的執行緒由於要等待IO或者其他原因(比如呼叫sleep方法)被阻塞了,但是又沒有釋放鎖,其他執行緒便只能乾巴巴地等待,試想一下,這多麼影響程式執行效率。
因此就需要有一種機制可以不讓等待的執行緒一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到。
例子2:
當有多個執行緒讀寫檔案時,讀操作和寫操作會發生衝突現象,寫操作和寫操作會發生衝突現象,但是讀操作和讀操作不會發生衝突現象。
但是採用synchronized關鍵字來實現同步的話,就會導致一個問題:
如果多個執行緒都只是進行讀操作,當一個執行緒在進行讀操作時,其他執行緒只能等待無法進行讀操作。
因此就需要一種機制來使得多個執行緒都只是進行讀操作時,執行緒之間不會發生衝突,通過Lock就可以辦到。
另外,通過Lock可以知道執行緒有沒有成功獲取到鎖。這個是synchronized無法辦到的。
總的來說,也就是說Lock提供了比synchronized更多的功能。
2.lock
lock和synchronized的區別
(1)lock不是java語言內建的,synchronized是java語言的關鍵字,因此是內建特性。lock是一個類,通過這個類可以實現同步訪問;
(2)lock和synchronized有一點非常大的不同,採用synchronized不需要使用者手動的去釋放鎖,當synchronized方法或者程式碼塊執行完畢之後,系統會自動的讓執行緒釋放對鎖的佔有,而lock則必須要使用者去手動釋放鎖,如果沒有主動的釋放鎖,就會可能導致出現死鎖的現象
LOCK
首先要說明的就是,通過檢視LOCK 的原始碼可知道,lock是一個介面
lock介面中每個方法的使用:lock()、tryLock()、tryLock(long time, TimeUnit unit)、lockInterruptibly()是用來獲取鎖的。 unLock()方法是用來釋放鎖的。
四個獲取鎖方法的區別:
(1)lock()方法時平常使用的最多的一個方法,就是用來獲取鎖的,如果鎖已經被其他執行緒獲取,則進行等待。
由於在前面講到如果採用lock,必須主動去釋放鎖,並且在發生異常時,不會自動釋放鎖。因此一般來說,使用lock必須在try{}catch{}塊中進行,並且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被釋放掉,房主死鎖的發生。
(2)tryLock()方法是有返回值的,他表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已經被其他執行緒獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在哪裡等待
(3)tryLock(long time, TimeUnit unit)方法和tryLock()方法時類似的,只不過區別在於這個方法在拿不到鎖時會等待一定時間,在時間限制之內如果還是拿不到鎖,就返回false。如果一開始就拿到鎖或者在等待期間內拿到了鎖,則就返回true。
(4)lockinterruptibly()方法比較特殊,當通過這個方法區獲取鎖時,如果執行緒正在等待獲取鎖,則這個執行緒能夠相應中斷,即中斷執行緒的等待狀態。也就是說,當連個執行緒同時通過lock.lockinterruputibly()向獲取某個鎖時,假如此時執行緒A獲取到了鎖,而執行緒B只有等待,那麼對執行緒呼叫threadB.interrupt()方法能夠中斷執行緒B的等待過程。
注意:當一個執行緒獲取了鎖之後,是不會被interrupt()方法中斷的
因此當通過lockinterruptibly()方法獲取某個鎖時,如果不能獲取到,只有進行等待的情況下,是可以相應中斷的
而用synchronized修飾的話,當一個執行緒處於等待某個鎖的狀態,是無法被中斷的,只有一直等待下去。
ReentrantLock
直接使用lock介面的話,我們需要實現很多方法,不太方便,ReentrantLock是唯一實現了Lock介面的類,並且ReentrantLock提供了更多的方法,ReentrantLock,意思是“可重入鎖”。
ReadWriteLock也是一個介面,在它裡面只定義了兩個方法:
一個用來獲取讀鎖,一個用來獲取寫鎖。也就是說將檔案的讀寫操作分開,分成2個鎖來分配給執行緒,從而使得多個執行緒可以同時進行讀操作。ReentrantReadWriteLock實現了ReadWriteLock介面。
ReentrantReadWriteLock
ReentrantReadWriteLock裡面提供了很多豐富的方法,不過最主要的兩個方法:readlock()和writelock用來獲取讀鎖和寫鎖
注意:
不過要注意的是,如果有一個執行緒已經佔用了讀鎖,則此時其他執行緒如果要申請寫鎖,則申請寫鎖的執行緒會一直等待釋放讀鎖。
如果有一個執行緒已經佔用了寫鎖,則此時其他執行緒如果申請寫鎖或者讀鎖,則申請的執行緒會一直等待釋放寫鎖。
3.LOCK和SYNCHRONIZED的選擇
(1)lock是一個介面,而synchronized是java的關鍵字,synchronized是內建的語言實現;
(2)synchronized在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;而lock在發生異常時,如果沒有主動通過unlock()去釋放鎖,則很可能造成死鎖現象,因此使用lock()時需要在finally塊中釋放鎖;
(3)lock可以讓等待鎖的執行緒響應中斷,而synchronized卻不行,使用synchronized時,等待的執行緒會一直等待下去,不訥訥狗狗響應中斷
(4)通過lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到
(5)lock可以提高多個執行緒進行讀操作的效率
在效能上來說,如果競爭資源不激烈,兩者的效能是差不多的,而競爭資源非常激烈是(既有大量執行緒同時競爭),此時lock的效能要遠遠優於synchronized。所以說,在具體使用時適當情