Lock的使用
前言
這是java多線程基礎理論知識的第6篇文章,預計這一系列的分享還有2篇。今天要分享的內容是進階版的線程鎖,之前的線程鎖主要是synchronized關鍵字的方法或者語句塊,這種用法比較生澀和不友好;本章推薦使用Lock對象來替換synchronized,因為Lock類有很多獨特的優(yōu)勢,而且還有助于學習并發(fā)包中的源碼。
主要內容如下:

ReentrantLock類
ReentrantLock
聲明方式如下:
private Lock lock = new ReentrantLock();
使用方式如下:
//程序片段
try{
//獲得鎖
lock.lock();
...
}catch(InterruptedException e){
e.printStackTrace();
}finally {
//釋放鎖
lock.unlock();
}
調用lock.lock()代碼的線程就持有了“對象監(jiān)視器”,其他線程只有等待鎖被釋放時再次爭搶。和synchronized關鍵字一樣,線程簡間的執(zhí)行順序是隨機的。
Conditon的使用
關鍵字synchronized與wait()和notify()/notifyAll()方法相結合可以實現等待/通知模式。類ReentrantLock也具有相同的功能,但需要借助Condition對象,該對象支持多路通知功能,就是在一個Lock對象里可以創(chuàng)建多個Condition(對象監(jiān)視器)實例,線程對象可以注冊在指定的Condition中,從而可以有選擇地進行線程通知,在線程調度上更加靈活。
聲明方式如下:
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
使用方式如下:
try{
lock.lock();
...
//等待
condition.await();
...
}catch(InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
注意,在condition.await()和condition.signal()方法調用前必須調用lock.lock()方法獲得同步監(jiān)視器。
總結:
- Object類中的wait()(代碼行處釋放鎖)方法相當于Condition類中的await()方法。
- Object類中的wait(long timeout)方法相當于Condition類中的await(long time,TimeUnit unit)方法。
- Object類中的notify()(執(zhí)行完釋放鎖)方法相當于Condition類中的signal()方法。
- Object類中的notifyAll()方法相當于Condition類中的signalAll()方法。
公平鎖與非公平鎖
注
- FIFO:先來先得,先進先出。
- 搶占機制:隨機獲得鎖。
使用方式(true為公平鎖,false為非公平鎖):
lock = new ReentrantLock(boolean)
公平鎖的執(zhí)行結果是呈基本有序的,不是完全有序;非公平鎖的執(zhí)行結果是基本亂序的。
方法大總結
這里用表格列舉一下,lock經常使用的方法。
| 方法名 | 參數 | 描述 |
|---|---|---|
| getHoldCount | / | 查詢當前線程保持此鎖定的個數,也就是調用lock()方法的個數。 |
| getQueueLength | / | 返回正等待獲取此鎖定的線程估計數。 |
| getWaitQueueLength | Condition | 等待與此鎖定相關的給定條件Condition的線程估計數 。 |
| hasQueuedThread | Thread | 查詢指定的線程是否等待獲取此鎖。 |
| hasQueuedThreads | / | 查詢是否有線程等待獲取此鎖。 |
| hasWaiters | Condition | 查詢是否有線程正在等待與此鎖有關的Condition條件。 |
| isFair | / | 判斷是不是公平鎖 ,ReentrantLock默認使用非公平鎖。 |
| isHeldByCurrentThread | / | 查詢當前線程是否保持此鎖定。 |
| isLocked | / | 查詢此鎖定是否由任意線程保持。 |
| lockInterruptibly | / | 如果當前線程未被中斷,則獲取鎖定;如果中斷則拋出異常。 |
| tryLock | / | 僅僅在調用時鎖定未被另一個線程保持的鎖。 |
| tryLock | long,TimeUnit | 如果鎖在給定的時間內沒有被另一個線程保持,且當前線程未被中斷,則獲取該鎖定。 |
| awaitUninterruptibly | / | 忽略中斷的await方法,不拋出異常。 |
| awaitUntil | 時間戳 | 線程在等待時間到達前,可以被其他線程提前喚醒的await方法。 |
ReentrantReadWriteLock類
ReentrantLock類具有完全互斥的效果,即同一時間只能有同一個線程在執(zhí)行ReentrantLock.lock()方法后面的任務。這樣做雖然很安全,但是效率很低下,這里介紹一種讀寫鎖ReentrantReadWriteLock類,讀寫鎖表示有兩個鎖,一個是讀相關操作的鎖,也稱共享鎖;一個是寫相關操作的鎖,也稱排他鎖。
共享鎖聲明方式如下:
lock.readLock().lock()
lock.readLock().unlock()
排他鎖聲明方式如下:
lock.writeLock().lock()
lock.writeLock().unlock()
讀讀共享
共享鎖允許多個線程同時執(zhí)行l(wèi)ock()方法后面的代碼。
寫寫互斥
互斥鎖同一時間只允許一個線程執(zhí)行l(wèi)ock()方法后面的代碼。
讀寫互斥
讀寫操作是互斥的,讀完之后才能寫。
寫讀互斥
寫讀操作是互斥的,寫完之后才能讀。只要出現寫操作鎖,就都是互斥的。