詳解Java并發(fā)之Condition
在使用Lock之前,我們使用的最多的同步方式應(yīng)該是synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)同步方式了。配合Object的wait()、notify()系列方法可以實(shí)現(xiàn)等待/通知模式。Condition接口也提供了類(lèi)似Object的監(jiān)視器方法,與Lock配合可以實(shí)現(xiàn)等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的。Object和Condition接口的一些對(duì)比。摘自《Java并發(fā)編程的藝術(shù)》

一、Condition接口介紹和示例
首先我們需要明白condition對(duì)象是依賴(lài)于lock對(duì)象的,意思就是說(shuō)condition對(duì)象需要通過(guò)lock對(duì)象進(jìn)行創(chuàng)建出來(lái)(調(diào)用Lock對(duì)象的newCondition()方法)。consition的使用方式非常的簡(jiǎn)單。但是需要注意在調(diào)用方法前獲取鎖。
package com.ydl.test.juc;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ConditionUseCase { public Lock lock = new ReentrantLock(); public Condition condition = lock.newCondition(); public static void main(String[] args) { ConditionUseCase useCase = new ConditionUseCase(); ExecutorService executorService = Executors.newFixedThreadPool (2); executorService.execute(new Runnable() { @Override public void run() {useCase.conditionWait(); } }); executorService.execute(new Runnable() { @Override public void run() {useCase.conditionSignal(); } }); } public void conditionWait() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + '拿到鎖了'); System.out.println(Thread.currentThread().getName() + '等待信號(hào)'); condition.await(); System.out.println(Thread.currentThread().getName() + '拿到信號(hào)'); }catch (Exception e){ }finally { lock.unlock(); } } public void conditionSignal() { lock.lock(); try { Thread.sleep(5000); System.out.println(Thread.currentThread().getName() + '拿到鎖了'); condition.signal(); System.out.println(Thread.currentThread().getName() + '發(fā)出信號(hào)'); }catch (Exception e){ }finally { lock.unlock(); } }}
pool-1-thread-1拿到鎖了pool-1-thread-1等待信號(hào)pool-1-thread-2拿到鎖了pool-1-thread-2發(fā)出信號(hào)pool-1-thread-1拿到信號(hào)
如示例所示,一般都會(huì)將Condition對(duì)象作為成員變量。當(dāng)調(diào)用await()方法后,當(dāng)前線(xiàn)程會(huì)釋放鎖并在此等待,而其他線(xiàn)程調(diào)用Condition對(duì)象的signal()方法,通知當(dāng)前線(xiàn)程后,當(dāng)前線(xiàn)程才從await()方法返回,并且在返回前已經(jīng)獲取了鎖。
二、Condition接口常用方法
condition可以通俗的理解為條件隊(duì)列。當(dāng)一個(gè)線(xiàn)程在調(diào)用了await方法以后,直到線(xiàn)程等待的某個(gè)條件為真的時(shí)候才會(huì)被喚醒。這種方式為線(xiàn)程提供了更加簡(jiǎn)單的等待/通知模式。Condition必須要配合鎖一起使用,因?yàn)閷?duì)共享狀態(tài)變量的訪(fǎng)問(wèn)發(fā)生在多線(xiàn)程環(huán)境下。一個(gè)Condition的實(shí)例必須與一個(gè)Lock綁定,因此Condition一般都是作為L(zhǎng)ock的內(nèi)部實(shí)現(xiàn)。
await() :造成當(dāng)前線(xiàn)程在接到信號(hào)或被中斷之前一直處于等待狀態(tài)。
await(long time, TimeUnit unit) :造成當(dāng)前線(xiàn)程在接到信號(hào)、被中斷或到達(dá)指定等待時(shí)間之前一直處于等待狀態(tài)。
awaitNanos(long nanosTimeout) :造成當(dāng)前線(xiàn)程在接到信號(hào)、被中斷或到達(dá)指定等待時(shí)間之前一直處于等待狀態(tài)。返回值表示剩余時(shí)間,如果在nanosTimesout之前喚醒,那么返回值 = nanosTimeout - 消耗時(shí)間,如果返回值 <= 0 ,則可以認(rèn)定它已經(jīng)超時(shí)了。
awaitUninterruptibly() :造成當(dāng)前線(xiàn)程在接到信號(hào)之前一直處于等待狀態(tài)。【注意:該方法對(duì)中斷不敏感】。
awaitUntil(Date deadline) :造成當(dāng)前線(xiàn)程在接到信號(hào)、被中斷或到達(dá)指定最后期限之前一直處于等待狀態(tài)。如果沒(méi)有到指定時(shí)間就被通知,則返回true,否則表示到了指定時(shí)間,返回返回false。
signal() :?jiǎn)拘岩粋€(gè)等待線(xiàn)程。該線(xiàn)程從等待方法返回前必須獲得與Condition相關(guān)的鎖。
signal()All :?jiǎn)拘阉械却€(xiàn)程。能夠從等待方法返回的線(xiàn)程必須獲得與Condition相關(guān)的鎖。
三、Condition接口原理簡(jiǎn)單解析
Condition是AQS的內(nèi)部類(lèi)。每個(gè)Condition對(duì)象都包含一個(gè)隊(duì)列(等待隊(duì)列)。等待隊(duì)列是一個(gè)FIFO的隊(duì)列,在隊(duì)列中的每個(gè)節(jié)點(diǎn)都包含了一個(gè)線(xiàn)程引用,該線(xiàn)程就是在Condition對(duì)象上等待的線(xiàn)程,如果一個(gè)線(xiàn)程調(diào)用了Condition.await()方法,那么該線(xiàn)程將會(huì)釋放鎖、構(gòu)造成節(jié)點(diǎn)加入等待隊(duì)列并進(jìn)入等待狀態(tài)。等待隊(duì)列的基本結(jié)構(gòu)如下所示。

等待分為首節(jié)點(diǎn)和尾節(jié)點(diǎn)。當(dāng)一個(gè)線(xiàn)程調(diào)用Condition.await()方法,將會(huì)以當(dāng)前線(xiàn)程構(gòu)造節(jié)點(diǎn),并將節(jié)點(diǎn)從尾部加入等待隊(duì)列。新增節(jié)點(diǎn)就是將尾部節(jié)點(diǎn)指向新增的節(jié)點(diǎn)。節(jié)點(diǎn)引用更新本來(lái)就是在獲取鎖以后的操作,所以不需要CAS保證。同時(shí)也是線(xiàn)程安全的操作。
3.2、等待
當(dāng)線(xiàn)程調(diào)用了await方法以后。線(xiàn)程就作為隊(duì)列中的一個(gè)節(jié)點(diǎn)被加入到等待隊(duì)列中去了。同時(shí)會(huì)釋放鎖的擁有。當(dāng)從await方法返回的時(shí)候。一定會(huì)獲取condition相關(guān)聯(lián)的鎖。當(dāng)?shù)却?duì)列中的節(jié)點(diǎn)被喚醒的時(shí)候,則喚醒節(jié)點(diǎn)的線(xiàn)程開(kāi)始嘗試獲取同步狀態(tài)。如果不是通過(guò) 其他線(xiàn)程調(diào)用Condition.signal()方法喚醒,而是對(duì)等待線(xiàn)程進(jìn)行中斷,則會(huì)拋出InterruptedException異常信息。
3.3、通知
調(diào)用Condition的signal()方法,將會(huì)喚醒在等待隊(duì)列中等待最長(zhǎng)時(shí)間的節(jié)點(diǎn)(條件隊(duì)列里的首節(jié)點(diǎn)),在喚醒節(jié)點(diǎn)前,會(huì)將節(jié)點(diǎn)移到同步隊(duì)列中。當(dāng)前線(xiàn)程加入到等待隊(duì)列中如圖所示:

在調(diào)用signal()方法之前必須先判斷是否獲取到了鎖。接著獲取等待隊(duì)列的首節(jié)點(diǎn),將其移動(dòng)到同步隊(duì)列并且利用LockSupport喚醒節(jié)點(diǎn)中的線(xiàn)程。節(jié)點(diǎn)從等待隊(duì)列移動(dòng)到同步隊(duì)列如下圖所示:

被喚醒的線(xiàn)程將從await方法中的while循環(huán)中退出。隨后加入到同步狀態(tài)的競(jìng)爭(zhēng)當(dāng)中去。成功獲取到競(jìng)爭(zhēng)的線(xiàn)程則會(huì)返回到await方法之前的狀態(tài)。
四、總結(jié)
調(diào)用await方法后,將當(dāng)前線(xiàn)程加入Condition等待隊(duì)列中。當(dāng)前線(xiàn)程釋放鎖。否則別的線(xiàn)程就無(wú)法拿到鎖而發(fā)生死鎖。自旋(while)掛起,不斷檢測(cè)節(jié)點(diǎn)是否在同步隊(duì)列中了,如果是則嘗試獲取鎖,否則掛起。當(dāng)線(xiàn)程被signal方法喚醒,被喚醒的線(xiàn)程將從await()方法中的while循環(huán)中退出來(lái),然后調(diào)用acquireQueued()方法競(jìng)爭(zhēng)同步狀態(tài)。
五、利用Condition實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式
import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class BoundedQueue { private LinkedList<Object> buffer; //生產(chǎn)者容器 private int maxSize ; //容器最大值是多少 private Lock lock; private Condition fullCondition; private Condition notFullCondition; BoundedQueue(int maxSize){ this.maxSize = maxSize; buffer = new LinkedList<Object>(); lock = new ReentrantLock(); fullCondition = lock.newCondition(); notFullCondition = lock.newCondition(); } /** * 生產(chǎn)者 * @param obj * @throws InterruptedException */ public void put(Object obj) throws InterruptedException { lock.lock(); //獲取鎖 try { while (maxSize == buffer.size()){notFullCondition.await(); //滿(mǎn)了,添加的線(xiàn)程進(jìn)入等待狀態(tài) } buffer.add(obj); fullCondition.signal(); //通知 } finally { lock.unlock(); } } /** * 消費(fèi)者 * @return * @throws InterruptedException */ public Object get() throws InterruptedException { Object obj; lock.lock(); try { while (buffer.size() == 0){ //隊(duì)列中沒(méi)有數(shù)據(jù)了 線(xiàn)程進(jìn)入等待狀態(tài)fullCondition.await(); } obj = buffer.poll(); notFullCondition.signal(); //通知 } finally { lock.unlock(); } return obj; }}
以上就是詳解Java并發(fā)之Condition的詳細(xì)內(nèi)容,更多關(guān)于Java并發(fā)Condition的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. 解決spring @ControllerAdvice處理異常無(wú)法正確匹配自定義異常2. IntelliJ IDEA設(shè)置自動(dòng)提示功能快捷鍵的方法3. IDEA一鍵完成格式化、去除無(wú)用引用、編譯的操作4. JavaScript創(chuàng)建表格的方法5. PHP如何開(kāi)啟Opcache功能提升程序處理效率6. android H5本地緩存加載優(yōu)化的實(shí)戰(zhàn)7. python新手學(xué)習(xí)使用庫(kù)8. 詳解如何使用Net將HTML簡(jiǎn)歷導(dǎo)出為PDF格式9. ASP.NET MVC使用jQuery ui的progressbar實(shí)現(xiàn)進(jìn)度條10. PHP程序員簡(jiǎn)單的開(kāi)展服務(wù)治理架構(gòu)操作詳解(二)

網(wǎng)公網(wǎng)安備