亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java并發(fā)編程之阻塞隊(duì)列深入詳解

 更新時(shí)間:2021年10月11日 08:45:54   作者:春風(fēng)~十一載  
這篇文章主要介紹了詳解Java阻塞隊(duì)列(BlockingQueue)的實(shí)現(xiàn)原理,阻塞隊(duì)列是Java util.concurrent包下重要的數(shù)據(jù)結(jié)構(gòu),是一種特殊的隊(duì)列,需要的朋友可以參考下

1. 什么是阻塞隊(duì)列

 阻塞隊(duì)列是一種特殊的隊(duì)列,和數(shù)據(jù)結(jié)構(gòu)中普通的隊(duì)列一樣,也遵守先進(jìn)先出的原則同時(shí),阻塞隊(duì)列是一種能保證線程安全的數(shù)據(jù)結(jié)構(gòu),并且具有以下兩種特性:當(dāng)隊(duì)列滿的時(shí)候,繼續(xù)向隊(duì)列中插入元素就會(huì)讓隊(duì)列阻塞,直到有其他線程從隊(duì)列中取走元素;當(dāng)隊(duì)列為空的時(shí)候,繼續(xù)出隊(duì)列也會(huì)讓隊(duì)列阻塞,直到有其他線程往隊(duì)列中插入元素

補(bǔ)充:線程阻塞的意思指代碼此時(shí)不會(huì)被執(zhí)行,即操作系統(tǒng)在此時(shí)不會(huì)把這個(gè)線程調(diào)度到CPU上去執(zhí)行了

2. 阻塞隊(duì)列的代碼使用

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)部是基于鏈表方式來實(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í)線程進(jìn)入阻塞等待狀態(tài),什么也不會(huì)打印了,直到有其他線程給隊(duì)列中放入新的元素為止

3. 生產(chǎn)者消費(fèi)者模型

生產(chǎn)者消費(fèi)者模型是在服務(wù)器開發(fā)和后端開發(fā)中比較常用的編程手段,一般用于解耦合和削峰填谷。

高耦合度:兩個(gè)代碼模塊的關(guān)聯(lián)關(guān)系比較高
高內(nèi)聚:一個(gè)代碼模塊內(nèi)各個(gè)元素彼此結(jié)合的緊密
因此,我們一般追求高內(nèi)聚低耦合,這樣會(huì)加快執(zhí)行效率,而使用生產(chǎn)者消費(fèi)者模型就可以解耦合

(1)應(yīng)用一:解耦合

我們以實(shí)際生活中的情況為例,這里有兩臺(tái)服務(wù)器:A服務(wù)器和B服務(wù)器,當(dāng)A服務(wù)器傳輸數(shù)據(jù)給B時(shí),要是直接傳輸?shù)脑挘敲床皇茿向B推送數(shù)據(jù),就是B從A中拉取數(shù)據(jù),都是需要A和B直接交互,所以A和B存在依賴關(guān)系(A和B的耦合度比較高)。未來如果服務(wù)器需要擴(kuò)展,比如加一個(gè)C服務(wù)器,讓A給C傳數(shù)據(jù),那么改動(dòng)就比較復(fù)雜,且會(huì)降低效率。這時(shí)我們可以加一個(gè)隊(duì)列,這個(gè)隊(duì)列為阻塞隊(duì)列,如果A把數(shù)據(jù)寫到隊(duì)列里,B從中取,那么隊(duì)列相當(dāng)于是中轉(zhuǎn)站(或者說交易場(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)其中一組線程,而消費(fèi)者充當(dāng)另一組線程,而交易場(chǎng)所就可以使用阻塞隊(duì)列了

(2)應(yīng)用二:削峰填谷

在這里插入圖片描述

實(shí)際生活中
在河道中大壩算是一個(gè)很重要的組成部分了,如果沒有大壩,大家試想一下結(jié)果:當(dāng)汛期來臨后上游的水很大時(shí),下游就會(huì)涌入大量的水發(fā)生水災(zāi)讓莊稼被淹沒;而旱期的話下游的水會(huì)很少可能會(huì)引發(fā)旱災(zāi)。若有大壩的話,汛期時(shí)大壩把多余的水存到大壩中,關(guān)閘蓄水,讓上游的水按一定速率往下流,避免突然一波大雨把下游淹了,這樣下游不至于出現(xiàn)水災(zāi)。旱期時(shí)大壩把之前儲(chǔ)存好的水放出來,還是讓讓水按一定速率往下流,避免下流太缺水,這樣既可以避免汛期發(fā)生洪澇又可以避免旱期發(fā)生旱災(zāi)了。
峰:相當(dāng)于汛期
谷:相當(dāng)于旱期
計(jì)算機(jī)中
這樣的情況在計(jì)算機(jī)中也是很典型的,尤其是在服務(wù)器開發(fā)中,網(wǎng)關(guān)通常會(huì)把互聯(lián)網(wǎng)中的請(qǐng)求轉(zhuǎn)發(fā)給業(yè)務(wù)服務(wù)器,比如一些商品服務(wù)器,用戶服務(wù)器,商家服務(wù)器(存放商家的信息),直播服務(wù)器。但因?yàn)榛ヂ?lián)網(wǎng)過來的請(qǐng)求數(shù)量是多是少不可控,相當(dāng)于上游的水,如果突然來了一大波請(qǐng)求,網(wǎng)關(guān)即使能扛得住,后續(xù)的很多服務(wù)器收到很多請(qǐng)求也就會(huì)崩潰(處理一個(gè)請(qǐng)求涉及到一系列的數(shù)據(jù)庫操作,因?yàn)閿?shù)據(jù)庫相關(guān)操作效率本身比較低,這樣請(qǐng)求多了就處理不過來了,因此就會(huì)崩潰)

在這里插入圖片描述

所以實(shí)際情況中網(wǎng)關(guān)和業(yè)務(wù)服務(wù)器之間往往用一個(gè)隊(duì)列來緩沖,這個(gè)隊(duì)列就是阻塞隊(duì)列(交易場(chǎng)所),用這個(gè)隊(duì)列來實(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ù)器的安全。

(3)相關(guān)代碼

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<>();
        // 此線程作為消費(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();
        // 此線程作為生產(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)者通過sleep每過1秒生產(chǎn)一個(gè)元素,而消費(fèi)者不使用sleep,所以每當(dāng)生產(chǎn)一個(gè)元素時(shí),消費(fèi)者都會(huì)立馬消費(fèi)一個(gè)元素)

4.阻塞隊(duì)列和生產(chǎn)者消費(fèi)者模型功能的實(shí)現(xiàn)

在學(xué)會(huì)如何使用BlockingQueue后,那么如何自己去實(shí)現(xiàn)一個(gè)呢?
主要思路:

  • 1.利用數(shù)組
  • 2.head代表隊(duì)頭,tail代表隊(duì)尾
  • 3.head和tail重合后到底是空的還是滿的判斷方法:專門定義一個(gè)size記錄當(dāng)前隊(duì)列元素個(gè)數(shù),入隊(duì)列時(shí)size加1出隊(duì)列時(shí)size減1,當(dāng)size為0表示空,為數(shù)組最大長(zhǎng)度就是滿的(也可以浪費(fèi)一個(gè)數(shù)組空間用head和tail重合表示空,用tail+1和head重合表示滿,但此方法較為麻煩,上一個(gè)方法較為直觀,因此我們使用上一個(gè)方法)
public class Test2 {
    static class BlockingQueue {
    private int[] items = new int[1000];    // 此處的1000相當(dāng)于隊(duì)列的最大容量, 此處暫時(shí)不考慮擴(kuò)容的問題.
    private int head = 0;//定義隊(duì)頭
    private int tail = 0;//定義隊(duì)尾
    private int size = 0;//數(shù)組大小
    private Object locker = new Object();

    // put 用來入隊(duì)列
    public void put(int item) throws InterruptedException {
        synchronized (locker) {
            while (size == items.length) {
                // 隊(duì)列已經(jīng)滿了,阻塞隊(duì)列開始阻塞
                locker.wait();
            }
            items[tail] = item;
            tail++;
            // 如果到達(dá)末尾, 就回到起始位置.
            if (tail >= items.length) {
                tail = 0;
            }
            size++;
            locker.notify();
        }
    }
    // take 用來出隊(duì)列
    public int take() throws InterruptedException {
        int ret = 0;
        synchronized (locker) {
            while (size == 0) {
                // 對(duì)于阻塞隊(duì)列來說, 如果隊(duì)列為空, 再嘗試取元素, 就要阻塞
                locker.wait();
            }
            ret = items[head];
            head++;
            if (head >= items.length) {
                head = 0;
            }
            size--;
            // 此處的notify 用來喚醒 put 中的 wait
            locker.notify();
        }
        return ret;
    }
}

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue queue = new BlockingQueue();
        // 消費(fèi)者線程
        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)者線程
        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并發(fā)編程之阻塞隊(duì)列深入詳解的文章就介紹到這了,更多相關(guān)Java 阻塞隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JDK1.8中ArrayList是如何擴(kuò)容的

    JDK1.8中ArrayList是如何擴(kuò)容的

    本文基于此出發(fā)講解ArrayList的擴(kuò)容機(jī)制,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 詳解SpringBoot異常處理流程及原理

    詳解SpringBoot異常處理流程及原理

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著SpringBoot異常處理流程及原理展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • SpringBoot自定義全局異常處理器的問題總結(jié)

    SpringBoot自定義全局異常處理器的問題總結(jié)

    Springboot框架提供兩個(gè)注解幫助我們十分方便實(shí)現(xiàn)全局異常處理器以及自定義異常,處理器會(huì)優(yōu)先處理更具體的異常類型,如果沒有找到匹配的處理器,那么它會(huì)尋找處理更一般異常類型的處理器,本文介紹SpringBoot自定義全局異常處理器的問題,一起看看吧
    2024-01-01
  • java設(shè)計(jì)模式之建造者模式學(xué)習(xí)

    java設(shè)計(jì)模式之建造者模式學(xué)習(xí)

    建造者模式(Builder Pattern)主要用于“分步驟構(gòu)建一個(gè)復(fù)雜的對(duì)象”,在這其中“分步驟”是一個(gè)穩(wěn)定的算法,下面給出了詳細(xì)的示例
    2014-01-01
  • Java單例模式下的MongoDB數(shù)據(jù)庫操作工具類

    Java單例模式下的MongoDB數(shù)據(jù)庫操作工具類

    這篇文章主要介紹了Java單例模式下的MongoDB數(shù)據(jù)庫操作工具類,結(jié)合實(shí)例形式分析了java基于單例模式下操作MongoDB數(shù)據(jù)庫相關(guān)連接、查詢、插入、刪除等操作封裝技巧,需要的朋友可以參考下
    2018-01-01
  • 基于Spark實(shí)現(xiàn)隨機(jī)森林代碼

    基于Spark實(shí)現(xiàn)隨機(jī)森林代碼

    這篇文章主要為大家詳細(xì)介紹了基于Spark實(shí)現(xiàn)隨機(jī)森林代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • MyBatis-Plus allEq()的用法詳解

    MyBatis-Plus allEq()的用法詳解

    這篇文章主要介紹了MyBatis-Plus allEq()的用法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 基于SSM+Shiro+Bootstrap實(shí)現(xiàn)用戶權(quán)限管理系統(tǒng)

    基于SSM+Shiro+Bootstrap實(shí)現(xiàn)用戶權(quán)限管理系統(tǒng)

    這篇文章主要介紹了基于SSM+Shiro實(shí)現(xiàn)一個(gè)用戶權(quán)限管理系統(tǒng),每位用戶只可訪問指定的頁面,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,快跟隨小編一起學(xué)習(xí)吧
    2021-12-12
  • MyBatis之傳入?yún)?shù)為list、數(shù)組、map的寫法

    MyBatis之傳入?yún)?shù)為list、數(shù)組、map的寫法

    這篇文章主要介紹了MyBatis之傳入?yún)?shù)為list、數(shù)組、map的寫法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java使用正則表達(dá)式刪除所有HTML標(biāo)簽的方法示例

    Java使用正則表達(dá)式刪除所有HTML標(biāo)簽的方法示例

    這篇文章主要介紹了Java使用正則表達(dá)式刪除所有HTML標(biāo)簽的方法,結(jié)合完整實(shí)例形式分析了java針對(duì)HTML頁面元素script標(biāo)簽、style標(biāo)簽、html標(biāo)簽等的正則匹配相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06

最新評(píng)論