併發編程中的Condition接口


使用synchronized鎖時候,在線程運行期間,可以使用java.lang.Object上的方法實現等待通知模式,這些方法包括wait()、wait(long timeout)、notify()以及notifyAll()。

而使用Lock接口,同樣有類似的方法,這些方法提供在Condition接口中。包括await、signal()、signalAll等方法。獲取一個Condition必須通過Lock的newCondition()方法。

Object的wait() notify()方法使用回顧

Java.lang.Object 裡的三個方法包括wait() notify() notifyAll()。

wait()

導致當前線程等待,直到其他線程調用同步監視器的notify方法或notifyAll方法來喚醒該線程。

wait(mills)

都是等待指定時間後自動甦醒,調用wait方法的當前線程會釋放該同步監視器的鎖定,可以不用notify或notifyAll方法把它喚醒。

notify()

喚醒在同步監視器上等待的單個線程,如果所有線程都在同步監視器上等待,則會選擇喚醒其中一個線程,選擇是任意性的,只有當前線程放棄對該同步監視器的鎖定後,也就是使用wait方法後,才可以執行被喚醒的線程。

notifyAll()

喚醒在同步監視器上等待的所有的線程。只用當前線程放棄對該同步監視器的鎖定後,也就是使用wait方法後,才可以執行被喚醒的線程。

下面是一個簡單的例子,通過例子認識如何使用Object的wait、notify、notifyAll方法。

<code>public class ObjectWaitNotify implements Runnable{
public static void main(String[] args) throws InterruptedException {
ObjectWaitNotify ow = new ObjectWaitNotify();
Thread t1 = new Thread(ow);
Thread t2 = new Thread(ow);
Thread t3 = new Thread(ow);
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
synchronized(ow){
//ow.notify();//每次喚醒一個線程
ow.notifyAll();//喚醒全部等待線程
}
t1.join();
t2.join();
}
@Override
public synchronized void run() {
System.out.println(Thread.currentThread().getName()+"開始執行了");
try {
//調用wait方法等待
this.wait();
System.out.println(Thread.currentThread().getName()+"被喚醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"結束執行了");
}
}/<code>

Condition和ConditionObject

Condition定義了等待/通知兩種類型的方法,當前線程調用這些方法時,需要提前獲取到

Condition對象關聯的鎖。Condition對象是由Lock對象的newCondition()方法創建出來的,換句話說,Condition是依賴Lock對象的。

ConditionObject是AQS中的內部類,提供了條件鎖的同步實現,實現了Condition接口,並且實現了其中的await(),signal(),signalALL()等方法。

在一個AQS同步器中,可以定義多個Condition,只需要多次lock.newCondition(),每次都會返回一個新的ConditionObject對象。

在ConditionObject中,通過一個等待隊列來維護條線等待的線程。所以在一個同步器中可以有多個等待隊列,他們等待的條件是不一樣的。

等待隊列是一個FIFO的隊列,在隊列的每個節點都包含了一個線程引用。該線程就是在Condition對象上等待的線程。這裡的節點和AQS中的同步隊列中的節點一樣,使用的都是AbstractQueuedSynchronizer.Node類。每個調用了condition.await()的線程都會進入到等待隊列中去。

在Condition中包含了firstWaiter和lastWaiter,每次加入到等待隊列中的線程都會加入到等待隊列的尾部,來構成一個FIFO的等待隊列。

併發編程中的Condition接口

理解了基本原理之後,我們再通過簡單的例子瞭解下Condition是如何使用的:

<code>public class ConditionUseCase implements Runnable{
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
}
public void conditionSignal() throws InterruptedException {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ConditionUseCase cus = new ConditionUseCase();
new Thread(cus).start();
new Thread(cus).start();
Thread.sleep(1000);
//通知
cus.conditionSignal();
}
@Override
public void run() {
//等待
try {
System.out.println(Thread.currentThread().getName()+"我要等待了。");
conditionWait();//等待
System.out.println(Thread.currentThread().getName()+"等待了結束,重新啟程。");
} catch (InterruptedException e) {
e.printStackTrace();
}/<code>

}

}


分享到:


相關文章: