Java多線程 BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型詳解
BlockingQueue
BlockingQueue、解決了多線程中,如何高效安全“傳輸”數(shù)據(jù)的問題。程序員無需關(guān)心什么時(shí)候阻塞線程,什么時(shí)候喚醒線程,該喚醒哪個(gè)線程。
方法介紹

BlockingQueue是Queue的子類
void put(E e)
插入指定元素,當(dāng)BlockingQueue為滿,則線程阻塞,進(jìn)入Waiting狀態(tài),直到BlockingQueue有空閑空間再繼續(xù)。
這里以ArrayBlockingQueue為例進(jìn)行分析


void take()
隊(duì)首出隊(duì),當(dāng)BlockingQueue為空,則線程阻塞,進(jìn)入Waiting狀態(tài),直到BlockingQueue不為空再繼續(xù)。


int drainTo(Collection<? super E> c)
從隊(duì)列中批量取出數(shù)據(jù),并放入到另一個(gè)集合中,返回轉(zhuǎn)移數(shù)據(jù)的數(shù)量,只需一次加鎖和解鎖。
BlockingQueue的實(shí)現(xiàn)類
ArrayBlockingQueue
/* * Concurrency control uses the classic two-condition algorithm * found in any textbook. */ /** Main lock guarding all access */ final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ private final Condition notFull;
基于數(shù)組實(shí)現(xiàn)的BlockingQueue,需要指定隊(duì)列容量,可以指定是否為公平鎖;只有一個(gè)ReentrantLock,生產(chǎn)者和消費(fèi)者不能異步執(zhí)行。
LinkedBlockingQueue
/** Lock held by take, poll, etc */ private final ReentrantLock takeLock = new ReentrantLock(); /** Wait queue for waiting takes */ private final Condition notEmpty = takeLock.newCondition(); /** Lock held by put, offer, etc */ private final ReentrantLock putLock = new ReentrantLock(); /** Wait queue for waiting puts */ private final Condition notFull = putLock.newCondition();
基于鏈表實(shí)現(xiàn)的BlockingQueue,可以指定隊(duì)列容量,不指定隊(duì)列容量默認(rèn)為Integer.MAX_VALUE;有兩個(gè)ReentrantLock,生產(chǎn)者和消費(fèi)者可以異步執(zhí)行。
BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型
緩沖區(qū)可以存放大量數(shù)據(jù)
生產(chǎn)者和消費(fèi)者速度各不相同
public class MyThread42 {
public static void main(String[] args)
{
final BlockingQueue<String> bq = new ArrayBlockingQueue<String>(10);
Runnable producerRunnable = new Runnable()
{
int i = 0;
public void run()
{
while (true)
{
try
{
System.out.println("我生產(chǎn)了一個(gè)" + i++);
bq.put(i + "");
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
};
Runnable customerRunnable = new Runnable()
{
public void run()
{
while (true)
{
try
{
System.out.println("我消費(fèi)了一個(gè)" + bq.take());
Thread.sleep(3000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
};
Thread producerThread = new Thread(producerRunnable);
Thread customerThread = new Thread(customerRunnable);
producerThread.start();
customerThread.start();
}
}
輸出結(jié)果如下
我生產(chǎn)了一個(gè)0 我消費(fèi)了一個(gè)1 我生產(chǎn)了一個(gè)1 我生產(chǎn)了一個(gè)2 我消費(fèi)了一個(gè)2 我生產(chǎn)了一個(gè)3 我生產(chǎn)了一個(gè)4 我生產(chǎn)了一個(gè)5 我消費(fèi)了一個(gè)3 我生產(chǎn)了一個(gè)6 我生產(chǎn)了一個(gè)7 我生產(chǎn)了一個(gè)8 我消費(fèi)了一個(gè)4 我生產(chǎn)了一個(gè)9 我生產(chǎn)了一個(gè)10 我生產(chǎn)了一個(gè)11 我消費(fèi)了一個(gè)5 我生產(chǎn)了一個(gè)12 我生產(chǎn)了一個(gè)13 我生產(chǎn)了一個(gè)14 我消費(fèi)了一個(gè)6 我生產(chǎn)了一個(gè)15 我生產(chǎn)了一個(gè)16 我消費(fèi)了一個(gè)7 我生產(chǎn)了一個(gè)17 我消費(fèi)了一個(gè)8 我生產(chǎn)了一個(gè)18 我消費(fèi)了一個(gè)9 我生產(chǎn)了一個(gè)19 我消費(fèi)了一個(gè)10 我生產(chǎn)了一個(gè)20 我消費(fèi)了一個(gè)11 我生產(chǎn)了一個(gè)21 我消費(fèi)了一個(gè)12 我生產(chǎn)了一個(gè)22 我消費(fèi)了一個(gè)13 我生產(chǎn)了一個(gè)23 我消費(fèi)了一個(gè)14 我生產(chǎn)了一個(gè)24 ······
生產(chǎn)者沒有生產(chǎn)到BlockingQueue的容量(極限是10)之前,生產(chǎn)3個(gè),消費(fèi)1個(gè),再生產(chǎn)到BlockingQueue的容量之后,生產(chǎn)一個(gè)消費(fèi)一個(gè),因?yàn)椴荒艹^BlockingQueue的容量。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
idea構(gòu)建web項(xiàng)目的超級(jí)詳細(xì)教程
好多朋友在使用IDEA創(chuàng)建項(xiàng)目時(shí),總會(huì)碰到一些小問題,下面這篇文章主要給大家介紹了關(guān)于idea構(gòu)建web項(xiàng)目的超級(jí)詳細(xì)教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
Spring boot實(shí)現(xiàn)上傳文件到本地服務(wù)器
這篇文章主要為大家詳細(xì)介紹了Spring boot實(shí)現(xiàn)上傳文件到本地服務(wù)器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
Java8 實(shí)現(xiàn)stream將對(duì)象集合list中抽取屬性集合轉(zhuǎn)化為map或list
這篇文章主要介紹了Java8 實(shí)現(xiàn)stream將對(duì)象集合list中抽取屬性集合轉(zhuǎn)化為map或list的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02
java 實(shí)現(xiàn)漢諾塔詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了java 實(shí)現(xiàn)漢諾塔詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04
java理論基礎(chǔ)Stream管道流狀態(tài)與并行操作
這篇文章主要為大家介紹了java理論基礎(chǔ)Stream管道流狀態(tài)與并行操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
Java使用EasyExcel動(dòng)態(tài)添加自增序號(hào)列
本文將介紹如何通過使用EasyExcel自定義攔截器實(shí)現(xiàn)在最終的Excel文件中新增一列自增的序號(hào)列,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Spring注解@Qualifier的詳細(xì)用法你知道幾種
本文給大家分享Spring注解@Qualifier的詳細(xì)用法,包括@Autowired和@Resource區(qū)別介紹,本文通過示例代碼給大家詳細(xì)介紹,感興趣的朋友跟隨小編一起看看吧2021-07-07
如何使用Mockito調(diào)用靜態(tài)方法和void方法
這篇文章主要介紹了如何使用Mockito調(diào)用靜態(tài)方法和void方法的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

