更多內(nèi)容,歡迎關(guān)注微信公眾號(hào):全菜工程師小輝~
Lock接口的實(shí)現(xiàn)類
ReentrantLock是實(shí)現(xiàn)了Lock接口的類,屬于獨(dú)享鎖,獨(dú)享鎖在同一時(shí)刻僅有一個(gè)線程可以進(jìn)行訪問(wèn)。Lock接口很簡(jiǎn)單,實(shí)現(xiàn)了如下:
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
lock()和unlock()
lock()和unlock()必須成對(duì)出現(xiàn)。一般來(lái)說(shuō),使用lock必須在try{}塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放,防止死鎖的發(fā)生。通常使用ReentrantLock來(lái)進(jìn)行同步的話,是以下面這種形式去使用的:
ReentrantLock lock = ...; lock.lock(); try{ //處理任務(wù) }catch(Exception ex){ }finally{ lock.unlock(); //釋放鎖 }
tryLock()
tryLock()方法是有返回值的,它表示用來(lái)嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false,也就說(shuō)這個(gè)方法無(wú)論如何都會(huì)立即返回。在拿不到鎖時(shí)不會(huì)一直在那等待。
tryLock(long time, TimeUnit unit)
tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過(guò)區(qū)別在于這個(gè)方法在拿不到鎖時(shí)會(huì)等待一定的時(shí)間,在時(shí)間期限之內(nèi)如果還拿不到鎖,就返回false。如果如果一開(kāi)始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true。
lockInterruptibly()
這是一個(gè)可中斷的獲取鎖方法。
如果沒(méi)有其他線程持有鎖,則當(dāng)前線程獲取到鎖,并為鎖計(jì)數(shù)加1,并且立即返回;如果當(dāng)前線程已經(jīng)持有鎖,則為鎖計(jì)數(shù)加1,并立即返回。
如果其他線程持有鎖,則當(dāng)前線程將休眠直到下面兩個(gè)事件中的一個(gè)發(fā)生:
- 當(dāng)前線程獲取到鎖
- 其他線程中斷當(dāng)前線程
簡(jiǎn)而言之,lockInterruptibly()在線程等待鎖期間是可以被interrupt的,而lock()在多線程等待鎖期間是無(wú)視interrupt的。
實(shí)驗(yàn)示例:
class MyThread { public static void main(String[] args) throws InterruptedException { final Lock lock = new ReentrantLock(); lock.lock(); Thread t1 = new Thread(() -> { lock.lock(); //注釋這行把下面注釋打開(kāi)再試試 // try { // lock.lockInterruptibly(); // } catch (InterruptedException e) { // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + " interrupted."); }); t1.start(); Thread.sleep(1000); t1.interrupt(); Thread.sleep(3000); } }
注意,當(dāng)一個(gè)線程獲取了鎖之后,是不會(huì)被interrupt()方法中斷的。因?yàn)閱为?dú)調(diào)用interrupt()方法不能中斷正在運(yùn)行過(guò)程中的線程,只能中斷阻塞過(guò)程中的線程。
ReadWriteLock接口的實(shí)現(xiàn)類
ReadWriteLock也是一個(gè)接口,在它里面只定義了兩個(gè)方法:
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock(); }
一個(gè)用來(lái)獲取讀鎖,一個(gè)用來(lái)獲取寫(xiě)鎖。也就是說(shuō)將文件的讀寫(xiě)操作分開(kāi),分成2個(gè)鎖來(lái)分配給線程,從而使得多個(gè)線程可以同時(shí)進(jìn)行讀操作。ReentrantReadWriteLock實(shí)現(xiàn)了ReadWriteLock接口。
線程進(jìn)入讀鎖的前提條件:
- 沒(méi)有其他線程的寫(xiě)鎖,
- 沒(méi)有寫(xiě)請(qǐng)求或者有寫(xiě)請(qǐng)求,但調(diào)用線程和持有鎖的線程是同一個(gè)
- 線程進(jìn)入寫(xiě)鎖的前提條件:
- 沒(méi)有其他線程的讀鎖
- 沒(méi)有其他線程的寫(xiě)鎖
ReentrantReadWriteLock支持以下功能:
- 支持公平和非公平的獲取鎖的方式;
- 支持可重入。讀線程在獲取了讀鎖后還可以獲取讀鎖;寫(xiě)線程在獲取了寫(xiě)鎖之后既可以再次獲取寫(xiě)鎖又可以獲取讀鎖;
- 還允許從寫(xiě)入鎖降級(jí)為讀取鎖,其實(shí)現(xiàn)方式是:先獲取寫(xiě)入鎖,然后獲取讀取鎖,最后釋放寫(xiě)入鎖。但是,從讀取鎖升級(jí)到寫(xiě)入鎖是不允許的;
- 讀取鎖和寫(xiě)入鎖都支持鎖獲取期間的中斷;
- Condition支持。僅寫(xiě)入鎖提供了一個(gè) Conditon 實(shí)現(xiàn);讀取鎖不支持 Conditon ,readLock().newCondition() 會(huì)拋出 UnsupportedOperationException。
ReentrantReadWriteLock里面提供了很多豐富的方法,不過(guò)最主要的有兩個(gè)方法:readLock()和writeLock()用來(lái)獲取讀鎖和寫(xiě)鎖。
使用實(shí)例:
public class Test { private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); public static void main(String[] args) { final Test test = new Test(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); } // 如果將函數(shù)改成synchronized的,會(huì)降低多線程的執(zhí)行效率 public void get(Thread thread) { rwl.readLock().lock(); try { long start = System.currentTimeMillis(); while(System.currentTimeMillis() - start <= 1) { System.out.println(thread.getName()+"正在進(jìn)行讀操作"); } System.out.println(thread.getName()+"讀操作完畢"); } finally { rwl.readLock().unlock(); } } }
Lock/ReadWriteLock和synchronized的區(qū)別:
總結(jié)來(lái)說(shuō),Lock和synchronized有以下幾點(diǎn)不同:
- Lock是一個(gè)接口,而synchronized是JAVA中的關(guān)鍵字,synchronized是內(nèi)置的語(yǔ)言實(shí)現(xiàn)
- synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線程占有的鎖,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生;而Lock在發(fā)生異常時(shí),如果沒(méi)有主動(dòng)通過(guò)unLock()去釋放鎖,則很可能造成死鎖現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖
- Lock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行,使用synchronized時(shí),等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷
- 通過(guò)Lock可以知道有沒(méi)有成功獲取鎖,而synchronized卻無(wú)法辦到
- Lock可以提高多個(gè)線程進(jìn)行讀操作的效率
官方表示,他們更支持synchronize,在未來(lái)的版本中還有優(yōu)化余地,所以還是提倡在synchronized能實(shí)現(xiàn)需求的情況下,優(yōu)先考慮使用synchronized來(lái)進(jìn)行同步。