java多線程系列(四)---ReentrantLock的使用
阿新 • • 發佈:2018-08-19
內心 method 另一個 isl exce inter write 並發 comm
Lock的使用
前言:本系列將從零開始講解java多線程相關的技術,內容參考於《java多線程核心技術》與《java並發編程實戰》等相關資料,希望站在巨人的肩膀上,再通過我的理解能讓知識更加簡單易懂。
目錄
- 認識cpu、核心與線程
- java多線程系列(一)之java多線程技能
- java多線程系列(二)之對象變量的並發訪問
- java多線程系列(三)之等待通知機制
- java多線程系列(四)之ReentrantLock的使用
- java多線程系列(五)之synchronized ReentrantLock volatile Atomic 原理分析
- java多線程系列(六)之線程池原理及其使用
ReentrantLocak特性(對比synchronized)
- 嘗試獲得鎖
- 獲取到鎖的線程能夠響應中斷
ReentrantLock(重入鎖)
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ (" " + (i + 1)));
}
lock.unlock();
}
}
效果和synchronized一樣,都可以同步執行,lock方法獲得鎖,unlock方法釋放鎖
await方法
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition=lock.newCondition();
public void testMethod() {
try {
lock.lock();
System.out.println("開始wait");
condition.await();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ (" " + (i + 1)));
}
} catch (InterruptedException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
}
- 通過創建Condition對象來使線程wait,必須先執行lock.lock方法獲得鎖
signal方法
public void signal()
{
try
{
lock.lock();
condition.signal();
}
finally
{
lock.unlock();
}
}
- condition對象的signal方法可以喚醒wait線程
創建多個condition對象
- 一個condition對象的signal(signalAll)方法和該對象的await方法是一一對應的,也就是一個condition對象的signal(signalAll)方法不能喚醒其他condition對象的await方法
- ReentrantLock類可以喚醒指定條件的線程,而object的喚醒是隨機的
Condition類和Object類
- Condition類的awiat方法和Object類的wait方法等效
- Condition類的signal方法和Object類的notify方法等效
- Condition類的signalAll方法和Object類的notifyAll方法等效
Lock的公平鎖和非公平鎖
Lock lock=new ReentrantLock(true);//公平鎖
Lock lock=new ReentrantLock(false);//非公平鎖
- 公平鎖指的是線程獲取鎖的順序是按照加鎖順序來的,而非公平鎖指的是搶鎖機制,先lock的線程不一定先獲得鎖。
ReentrantLock類的方法
- getHoldCount() 查詢當前線程保持此鎖的次數,也就是執行此線程執行lock方法的次數
- getQueueLength()返回正等待獲取此鎖的線程估計數,比如啟動10個線程,1個線程獲得鎖,此時返回的是9
- getWaitQueueLength(Condition condition)返回等待與此鎖相關的給定條件的線程估計數。比如10個線程,用同一個condition對象,並且此時這10個線程都執行了condition對象的await方法,那麽此時執行此方法返回10
- hasWaiters(Condition condition)查詢是否有線程等待與此鎖有關的給定條件(condition),對於指定contidion對象,有多少線程執行了condition.await方法
- hasQueuedThread(Thread thread)查詢給定線程是否等待獲取此鎖
- hasQueuedThreads()是否有線程等待此鎖
- isFair()該鎖是否公平鎖
- isHeldByCurrentThread() 當前線程是否保持鎖鎖定,線程的執行lock方法的前後分別是false和true
- isLock()此鎖是否有任意線程占用
- lockInterruptibly()如果當前線程未被中斷,獲取鎖
- tryLock()嘗試獲得鎖,僅在調用時鎖未被線程占用,獲得鎖
- tryLock(long timeout TimeUnit unit)如果鎖在給定等待時間內沒有被另一個線程保持,則獲取該鎖
tryLock和lock和lockInterruptibly的區別
- tryLock能獲得鎖就返回true,不能就立即返回false,tryLock(long timeout,TimeUnit unit),可以增加時間限制,如果超過該時間段還沒獲得鎖,返回false
- lock能獲得鎖就返回true,不能的話一直等待獲得鎖
- lock和lockInterruptibly,如果兩個線程分別執行這兩個方法,但此時中斷這兩個線程,前者不會拋出異常,而後者會拋出異常
讀寫鎖
- ReentrantLock是完全互斥排他的,這樣其實效率是不高的。
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("獲得讀鎖" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.readLock().unlock();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
- 讀鎖,此時多個線程可以或得讀鎖
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("獲得寫鎖" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.writeLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 寫鎖,此時只有一個線程能獲得寫鎖
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("獲得讀鎖" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.readLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("獲得寫鎖" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.writeLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 結果是讀鎖釋放後才執行寫鎖的方法,說明讀鎖和寫鎖是互斥的
總結
- Lock類也可以實現線程同步,而Lock獲得鎖需要執行lock方法,釋放鎖需要執行unLock方法
- Lock類可以創建Condition對象,Condition對象用來是線程等待和喚醒線程,需要註意的是Condition對象的喚醒的是用同一個Condition執行await方法的線程,所以也就可以實現喚醒指定類的線程
- Lock類分公平鎖和不公平鎖,公平鎖是按照加鎖順序來的,非公平鎖是不按順序的,也就是說先執行lock方法的鎖不一定先獲得鎖
- Lock類有讀鎖和寫鎖,讀讀共享,寫寫互斥,讀寫互斥
我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)
java多線程系列(四)---ReentrantLock的使用