轉載自https://www.cnblogs.com/jalja/p/5895051.html
一、Condition 類
在前面我們學習與synchronized鎖配合的線程等待(Object.wait)與線程通知(Object.notify),那么對於JDK1.5 的 java.util.concurrent.locks.ReentrantLock 鎖,JDK也為我們提供了與此功能相應的類java.util.concurrent.locks.Condition。Condition與重入鎖是通過lock.newCondition()方法產生一個與當前重入鎖綁定的Condtion實例,我們通知該實例來控制線程的等待與通知。該接口的所有方法:
public interface Condition {
//使當前線程加入 await() 等待隊列中,並釋放當鎖,當其他線程調用signal()會重新請求鎖。與Object.wait()類似。
void await() throws InterruptedException;
//調用該方法的前提是,當前線程已經成功獲得與該條件對象綁定的重入鎖,否則調用該方法時會拋出IllegalMonitorStateException。
//調用該方法后,結束等待的唯一方法是其它線程調用該條件對象的signal()或signalALL()方法。等待過程中如果當前線程被中斷,該方法仍然會繼續等待,同時保留該線程的中斷狀態。
void awaitUninterruptibly();
// 調用該方法的前提是,當前線程已經成功獲得與該條件對象綁定的重入鎖,否則調用該方法時會拋出IllegalMonitorStateException。
//nanosTimeout指定該方法等待信號的的最大時間(單位為納秒)。若指定時間內收到signal()或signalALL()則返回nanosTimeout減去已經等待的時間;
//若指定時間內有其它線程中斷該線程,則拋出InterruptedException並清除當前線程的打斷狀態;若指定時間內未收到通知,則返回0或負數。
long awaitNanos(long nanosTimeout) throws InterruptedException;
//與await()基本一致,唯一不同點在於,指定時間之內沒有收到signal()或signalALL()信號或者線程中斷時該方法會返回false;其它情況返回true。
boolean await(long time, TimeUnit unit) throws InterruptedException;
//適用條件與行為與awaitNanos(long nanosTimeout)完全一樣,唯一不同點在於它不是等待指定時間,而是等待由參數指定的某一時刻。
boolean awaitUntil(Date deadline) throws InterruptedException;
//喚醒一個在 await()等待隊列中的線程。與Object.notify()相似
void signal();
//喚醒 await()等待隊列中所有的線程。與object.notifyAll()相似
void signalAll();
}
二、使用
1、await() 等待 與 singnal()通知
1 package com.jalja.org.base.Thread;
2
3 import java.util.concurrent.TimeUnit;
4 import java.util.concurrent.locks.Condition;
5 import java.util.concurrent.locks.ReentrantLock;
6
7 /**
8 * Condition 配合Lock 實現線程的等待 與通知
9 */
10 public class ConditionTest{
11 public static ReentrantLock lock=new ReentrantLock();
12 public static Condition condition =lock.newCondition();
13 public static void main(String[] args) {
14 new Thread(){
15 @Override
16 public void run() {
17 lock.lock();//請求鎖
18 try{
19 System.out.println(Thread.currentThread().getName()+"==》進入等待");
20 condition.await();//設置當前線程進入等待
21 }catch (InterruptedException e) {
22 e.printStackTrace();
23 }finally{
24 lock.unlock();//釋放鎖
25 }
26 System.out.println(Thread.currentThread().getName()+"==》繼續執行");
27 }
28 }.start();
29 new Thread(){
30 @Override
31 public void run() {
32 lock.lock();//請求鎖
33 try{
34 System.out.println(Thread.currentThread().getName()+"=》進入");
35 Thread.sleep(2000);//休息2秒
36 condition.signal();//隨機喚醒等待隊列中的一個線程
37 System.out.println(Thread.currentThread().getName()+"休息結束");
38 }catch (InterruptedException e) {
39 e.printStackTrace();
40 }finally{
41 lock.unlock();//釋放鎖
42 }
43 }
44 }.start();
45 }
46 }
執行結果:
Thread-0==》進入等待 Thread-1=》進入 Thread-1休息結束 Thread-0==》繼續執行
流程:在調用await()方法前線程必須獲得重入鎖(第17行代碼),調用await()方法后線程會釋放當前占用的鎖。同理在調用signal()方法時當前線程也必須獲得相應重入鎖(代碼32行),調用signal()方法后系統會從condition.await()等待隊列中喚醒一個線程。當線程被喚醒后,它就會嘗試重新獲得與之綁定的重入鎖,一旦獲取成功將繼續執行。所以調用signal()方法后一定要釋放當前占用的鎖(代碼41行),這樣被喚醒的線程才能有獲得鎖的機會,才能繼續執行。
三、JDK中對Condition 的使用
我們來看看java.util.concurrent.ArrayBlockingQueue;
基於數組的阻塞隊列實現,在ArrayBlockingQueue內部,維護了一個定長數組,以便緩存隊列中的數據對象,這是一個常用的阻塞隊列,除了一個定長數組外,ArrayBlockingQueue內部還保存着兩個整形變量,分別標識着隊列的頭部和尾部在數組中的位置。
看看他的put方法:
public void put(E e) throws InterruptedException {
checkNotNull(e);//對傳入元素的null判斷
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//對put()方法做同步
try {
while (count == items.length)//如果隊列已滿
notFull.await();//讓當前添加元素的線程進入等待狀態
insert(e);// 如果有其他線程調用signal() 通知該線程 ,則進行添加行為
} finally {
lock.unlock();//釋放鎖
}
}
private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]);
items[takeIndex] = null;
takeIndex = inc(takeIndex);
--count;
notFull.signal();//喚醒一個在Condition等待隊列中的線程
return x;
}
版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/jiq408694711/article/details/51052592
是否釋放鎖:調用sleep和yield的時候不釋放當前線程所獲得的鎖,但是調用await/wait的時候卻釋放了其獲取的鎖並阻塞等待。
調用后何時恢復:
# sleep讓線程阻塞,且在指定的時間之內都不會執行,時間到了之后恢復到就緒狀態,也不一定被立即調度執行;
# yield只是讓當前對象回到就緒狀態,還是有可能馬上被再次被調用執行。
# await/wait,它會一直阻塞在條件隊列之上,之后某個線程調用對應的notify/signal方法,才會使得await/wait的線程回到就緒狀態,也是不一定立即執行。
誰的方法:yield和sleep方法都是Thread類的,而wait方法是Object類的,await方法是Condition顯示條件隊列的。
執行環境:yield和sleep方法可以放在線程中的任意位置,而await/wait方法必須放在同步塊里面,否則會產生運行時異常。
await/wait
Sleep
Yield
是否釋放持有的鎖
釋放
不釋放
不釋放
調用后何時恢復
喚醒后進入就緒態
指定時間后
立刻進入就緒態
誰的方法
Condition/Object
Thread
Thread
執行環境
同步代碼塊
任意位置
任意位置
---------------------
作者:小弟季義欽
來源:CSDN
原文:https://blog.csdn.net/jiyiqinlovexx/article/details/51052592
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

