這篇“Java中阻塞隊(duì)列怎么使用”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Java中阻塞隊(duì)列怎么使用”文章吧。
創(chuàng)新互聯(lián)建站為您提適合企業(yè)的網(wǎng)站設(shè)計(jì)?讓您的網(wǎng)站在搜索引擎具有高度排名,讓您的網(wǎng)站具備超強(qiáng)的網(wǎng)絡(luò)競(jìng)爭(zhēng)力!結(jié)合企業(yè)自身,進(jìn)行網(wǎng)站設(shè)計(jì)及把握,最后結(jié)合企業(yè)文化和具體宗旨等,才能創(chuàng)作出一份性化解決方案。從網(wǎng)站策劃到成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作, 我們的網(wǎng)頁(yè)設(shè)計(jì)師為您提供的解決方案。
阻塞隊(duì)列是一種特殊的隊(duì)列,和數(shù)據(jù)結(jié)構(gòu)中普通的隊(duì)列一樣,也遵守先進(jìn)先出的原則同時(shí),阻塞隊(duì)列是一種能保證線(xiàn)程安全的數(shù)據(jù)結(jié)構(gòu),并且具有以下兩種特性:當(dāng)隊(duì)列滿(mǎn)的時(shí)候,繼續(xù)向隊(duì)列中插入元素就會(huì)讓隊(duì)列阻塞,直到有其他線(xiàn)程從隊(duì)列中取走元素;當(dāng)隊(duì)列為空的時(shí)候,繼續(xù)出隊(duì)列也會(huì)讓隊(duì)列阻塞,直到有其他線(xiàn)程往隊(duì)列中插入元素
補(bǔ)充:線(xiàn)程阻塞的意思指代碼此時(shí)不會(huì)被執(zhí)行,即操作系統(tǒng)在此時(shí)不會(huì)把這個(gè)線(xiàn)程調(diào)度到CPU上去執(zhí)行了
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.BlockingDeque; public class Test { public static void main(String[] args) throws InterruptedException { //不能直接newBlockingDeque,因?yàn)樗且粋€(gè)接口,要向上轉(zhuǎn)型 //LinkedBlockingDeque內(nèi)部是基于鏈表方式來(lái)實(shí)現(xiàn)的 BlockingDeque<String> queue=new LinkedBlockingDeque<>(10);//此處可以指定一個(gè)具體的數(shù)字,這里的的10代表隊(duì)列的最大容量 queue.put("hello"); String elem=queue.take(); System.out.println(elem); elem=queue.take(); System.out.println(elem); } }
注意: put方法帶有阻塞功能,但是offer不具有,所以一般用put方法(能使用offer方法的原因是 BlockingDeque
繼承了Queue
)
打印結(jié)果如上所示,當(dāng)打印了hello后,隊(duì)列為空,代碼執(zhí)行到elem=queue.take();
就不會(huì)繼續(xù)往下執(zhí)行了,此時(shí)線(xiàn)程進(jìn)入阻塞等待狀態(tài),什么也不會(huì)打印了,直到有其他線(xiàn)程給隊(duì)列中放入新的元素為止
生產(chǎn)者消費(fèi)者模型是在服務(wù)器開(kāi)發(fā)和后端開(kāi)發(fā)中比較常用的編程手段,一般用于解耦合和削峰填谷。
高耦合度:兩個(gè)代碼模塊的關(guān)聯(lián)關(guān)系比較高
高內(nèi)聚:一個(gè)代碼模塊內(nèi)各個(gè)元素彼此結(jié)合的緊密
因此,我們一般追求高內(nèi)聚低耦合,這樣會(huì)加快執(zhí)行效率,而使用生產(chǎn)者消費(fèi)者模型就可以解耦合
我們以實(shí)際生活中的情況為例,這里有兩臺(tái)服務(wù)器:A服務(wù)器和B服務(wù)器,當(dāng)A服務(wù)器傳輸數(shù)據(jù)給B時(shí),要是直接傳輸?shù)脑?huà),那么不是A向B推送數(shù)據(jù),就是B從A中拉取數(shù)據(jù),都是需要A和B直接交互,所以A和B存在依賴(lài)關(guān)系(A和B的耦合度比較高)。未來(lái)如果服務(wù)器需要擴(kuò)展,比如加一個(gè)C服務(wù)器,讓A給C傳數(shù)據(jù),那么改動(dòng)就比較復(fù)雜,且會(huì)降低效率。這時(shí)我們可以加一個(gè)隊(duì)列,這個(gè)隊(duì)列為阻塞隊(duì)列,如果A把數(shù)據(jù)寫(xiě)到隊(duì)列里,B從中取,那么隊(duì)列相當(dāng)于是中轉(zhuǎn)站(或者說(shuō)交易場(chǎng)所),A相當(dāng)于生產(chǎn)者(提供數(shù)據(jù)),B相當(dāng)于消費(fèi)者(接收數(shù)據(jù)),此時(shí)就構(gòu)成了生產(chǎn)者消費(fèi)者模型,這樣會(huì)讓代碼耦合度更低,維護(hù)更方便,執(zhí)行效率更高。
在計(jì)算機(jī)中,生產(chǎn)者充當(dāng)其中一組線(xiàn)程,而消費(fèi)者充當(dāng)另一組線(xiàn)程,而交易場(chǎng)所就可以使用阻塞隊(duì)列了
在河道中大壩算是一個(gè)很重要的組成部分了,如果沒(méi)有大壩,大家試想一下結(jié)果:當(dāng)汛期來(lái)臨后上游的水很大時(shí),下游就會(huì)涌入大量的水發(fā)生水災(zāi)讓莊稼被淹沒(méi);而旱期的話(huà)下游的水會(huì)很少可能會(huì)引發(fā)旱災(zāi)。若有大壩的話(huà),汛期時(shí)大壩把多余的水存到大壩中,關(guān)閘蓄水,讓上游的水按一定速率往下流,避免突然一波大雨把下游淹了,這樣下游不至于出現(xiàn)水災(zāi)。旱期時(shí)大壩把之前儲(chǔ)存好的水放出來(lái),還是讓讓水按一定速率往下流,避免下流太缺水,這樣既可以避免汛期發(fā)生洪澇又可以避免旱期發(fā)生旱災(zāi)了。
峰:相當(dāng)于汛期
谷:相當(dāng)于旱期
計(jì)算機(jī)中
這樣的情況在計(jì)算機(jī)中也是很典型的,尤其是在服務(wù)器開(kāi)發(fā)中,網(wǎng)關(guān)通常會(huì)把互聯(lián)網(wǎng)中的請(qǐng)求轉(zhuǎn)發(fā)給業(yè)務(wù)服務(wù)器,比如一些商品服務(wù)器,用戶(hù)服務(wù)器,商家服務(wù)器(存放商家的信息),直播服務(wù)器。但因?yàn)榛ヂ?lián)網(wǎng)過(guò)來(lái)的請(qǐng)求數(shù)量是多是少不可控,相當(dāng)于上游的水,如果突然來(lái)了一大波請(qǐng)求,網(wǎng)關(guān)即使能扛得住,后續(xù)的很多服務(wù)器收到很多請(qǐng)求也就會(huì)崩潰(處理一個(gè)請(qǐng)求涉及到一系列的數(shù)據(jù)庫(kù)操作,因?yàn)閿?shù)據(jù)庫(kù)相關(guān)操作效率本身比較低,這樣請(qǐng)求多了就處理不過(guò)來(lái)了,因此就會(huì)崩潰)
所以實(shí)際情況中網(wǎng)關(guān)和業(yè)務(wù)服務(wù)器之間往往用一個(gè)隊(duì)列來(lái)緩沖,這個(gè)隊(duì)列就是阻塞隊(duì)列(交易場(chǎng)所),用這個(gè)隊(duì)列來(lái)實(shí)現(xiàn)生產(chǎn)者(網(wǎng)關(guān))消費(fèi)者(業(yè)務(wù)服務(wù)器)模型,把請(qǐng)求緩存到隊(duì)列中,后面的消費(fèi)者(業(yè)務(wù)服務(wù)器)按照自己固定的速率去讀請(qǐng)求。這樣當(dāng)請(qǐng)求很多時(shí),雖然隊(duì)列服務(wù)器可能會(huì)稍微受到一定壓力,但能保證業(yè)務(wù)服務(wù)器的安全。
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class TestDemo { public static void main(String[] args) { // 使用一個(gè) BlockingQueue 作為交易場(chǎng)所 BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(); // 此線(xiàn)程作為消費(fèi)者 Thread customer = new Thread() { @Override public void run() { while (true) { // 取隊(duì)首元素 try { Integer value = queue.take(); System.out.println("消費(fèi)元素: " + value); } catch (InterruptedException e) { e.printStackTrace(); } } } }; customer.start(); // 此線(xiàn)程作為生產(chǎn)者 Thread producer = new Thread() { @Override public void run() { for (int i = 1; i <= 10000; i++) { System.out.println("生產(chǎn)了元素: " + i); try { queue.put(i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; producer.start(); try { customer.join(); producer.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
打印如上(此代碼是讓生產(chǎn)者通過(guò)sleep每過(guò)1秒生產(chǎn)一個(gè)元素,而消費(fèi)者不使用sleep,所以每當(dāng)生產(chǎn)一個(gè)元素時(shí),消費(fèi)者都會(huì)立馬消費(fèi)一個(gè)元素)
在學(xué)會(huì)如何使用BlockingQueue
后,那么如何自己去實(shí)現(xiàn)一個(gè)呢?
主要思路:
1.利用數(shù)組
2.head代表隊(duì)頭,tail代表隊(duì)尾
3.head和tail重合后到底是空的還是滿(mǎn)的判斷方法:專(zhuān)門(mén)定義一個(gè)size記錄當(dāng)前隊(duì)列元素個(gè)數(shù),入隊(duì)列時(shí)size加1出隊(duì)列時(shí)size減1,當(dāng)size為0表示空,為數(shù)組最大長(zhǎng)度就是滿(mǎn)的(也可以浪費(fèi)一個(gè)數(shù)組空間用head和tail重合表示空,用tail+1和head重合表示滿(mǎn),但此方法較為麻煩,上一個(gè)方法較為直觀,因此我們使用上一個(gè)方法)
public class Test2 { static class BlockingQueue { private int[] items = new int[1000]; // 此處的1000相當(dāng)于隊(duì)列的最大容量, 此處暫時(shí)不考慮擴(kuò)容的問(wèn)題. private int head = 0;//定義隊(duì)頭 private int tail = 0;//定義隊(duì)尾 private int size = 0;//數(shù)組大小 private Object locker = new Object(); // put 用來(lái)入隊(duì)列 public void put(int item) throws InterruptedException { synchronized (locker) { while (size == items.length) { // 隊(duì)列已經(jīng)滿(mǎn)了,阻塞隊(duì)列開(kāi)始阻塞 locker.wait(); } items[tail] = item; tail++; // 如果到達(dá)末尾, 就回到起始位置. if (tail >= items.length) { tail = 0; } size++; locker.notify(); } } // take 用來(lái)出隊(duì)列 public int take() throws InterruptedException { int ret = 0; synchronized (locker) { while (size == 0) { // 對(duì)于阻塞隊(duì)列來(lái)說(shuō), 如果隊(duì)列為空, 再?lài)L試取元素, 就要阻塞 locker.wait(); } ret = items[head]; head++; if (head >= items.length) { head = 0; } size--; // 此處的notify 用來(lái)喚醒 put 中的 wait locker.notify(); } return ret; } } public static void main(String[] args) throws InterruptedException { BlockingQueue queue = new BlockingQueue(); // 消費(fèi)者線(xiàn)程 Thread consumer = new Thread() { @Override public void run() { while (true) { try { int elem = queue.take(); System.out.println("消費(fèi)元素: " + elem); } catch (InterruptedException e) { e.printStackTrace(); } } } }; consumer.start(); // 生產(chǎn)者線(xiàn)程 Thread producer = new Thread() { @Override public void run() { for (int i = 1; i < 10000; i++) { System.out.println("生產(chǎn)元素: " + i); try { queue.put(i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; producer.start(); consumer.join(); producer.join(); } }
運(yùn)行結(jié)果如上。
注意:
1.wait和notify的正確使用
2.put和take都會(huì)產(chǎn)生阻塞情況,但阻塞條件是對(duì)立的,wait不會(huì)同時(shí)觸發(fā)(put喚醒take阻塞,take喚醒put阻塞)
以上就是關(guān)于“Java中阻塞隊(duì)列怎么使用”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
名稱(chēng)欄目:Java中阻塞隊(duì)列怎么使用
轉(zhuǎn)載注明:http://m.newbst.com/article8/jeshop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作、網(wǎng)站設(shè)計(jì)公司、網(wǎng)站改版、面包屑導(dǎo)航、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)