手寫阻塞隊(duì)列以及JDK自帶阻塞隊(duì)列的API

1.什么是隊(duì)列

隊(duì)列是一種特殊的線性表,特殊之處在于它只允許在表的前端(front)進(jìn)行刪除操作,而在表的后端(rear)進(jìn)行插入操作,和棧一樣,隊(duì)列是一種操作受限制的線性表。進(jìn)行插入操作的端稱為隊(duì)尾,進(jìn)行刪除操作的端稱為隊(duì)頭。

隊(duì)列其實(shí)就是跟平時排隊(duì)一樣,按照順序來,先排隊(duì)的先買到東西,后排隊(duì)的后買到東西,排隊(duì)的第一個叫隊(duì)頭,最后一個叫隊(duì)尾,這就是隊(duì)列的先進(jìn)先出,這是和棧最大的區(qū)別。

2.什么是堵塞隊(duì)列?

當(dāng)隊(duì)列為空時,消費(fèi)者掛起,隊(duì)列已滿時,生產(chǎn)者掛起,這就是生產(chǎn)-消費(fèi)者模型,堵塞其實(shí)就是將線程掛起。
因?yàn)樯a(chǎn)者的生產(chǎn)速度和消費(fèi)者的消費(fèi)速度之間的不匹配,就可以通過堵塞隊(duì)列讓速度快的暫時堵塞,
如生產(chǎn)者每秒生產(chǎn)兩個數(shù)據(jù),而消費(fèi)者每秒消費(fèi)一個數(shù)據(jù),當(dāng)隊(duì)列已滿時,生產(chǎn)者就會堵塞(掛起),等待消費(fèi)者消費(fèi)后,再進(jìn)行喚醒。

堵塞隊(duì)列會通過掛起的方式來實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者之間的平衡,這是和普通隊(duì)列最大的區(qū)別。

3.如何實(shí)現(xiàn)堵塞隊(duì)列?

jdk其實(shí)已經(jīng)幫我們提供了實(shí)現(xiàn)方案,java5增加了concurrent包,concurrent包中的BlockingQueue就是堵塞隊(duì)列,我們不需要關(guān)心BlockingQueue如何實(shí)現(xiàn)堵塞,一切都幫我們封裝好了,只需要做一個沒有感情的API調(diào)用者就行。

4.BlockingQueue如何使用?

BlockingQueue本身只是一個接口,規(guī)定了堵塞隊(duì)列的方法,主要依靠幾個實(shí)現(xiàn)類實(shí)現(xiàn)。

4.1BlockingQueue主要方法

1.插入數(shù)據(jù)

(1)offer(E e):如果隊(duì)列沒滿,返回true,如果隊(duì)列已滿,返回false(不堵塞)
(2)offer(E e, long timeout, TimeUnit unit):可以設(shè)置等待時間,如果隊(duì)列已滿,則進(jìn)行等待。超過等待時間,則返回false
(3)put(E e):無返回值,一直等待,直至隊(duì)列空出位置

2.獲取數(shù)據(jù)

(1)poll():如果有數(shù)據(jù),出隊(duì),如果沒有數(shù)據(jù),返回null
(2)poll(long timeout, TimeUnit unit):可以設(shè)置等待時間,如果沒有數(shù)據(jù),則等待,超過等待時間,則返回null
(3)take():如果有數(shù)據(jù),出隊(duì)。如果沒有數(shù)據(jù),一直等待(堵塞)

4.2BlockingQueue主要實(shí)現(xiàn)類

1.ArrayBlockingQueue:ArrayBlockingQueue是基于數(shù)組實(shí)現(xiàn)的,通過初始化時設(shè)置數(shù)組長度,是一個有界隊(duì)列,而且ArrayBlockingQueue和LinkedBlockingQueue不同的是,ArrayBlockingQueue只有一個鎖對象,而LinkedBlockingQueue是兩個鎖對象,一個鎖對象會造成要么是生產(chǎn)者獲得鎖,要么是消費(fèi)者獲得鎖,兩者競爭鎖,無法并行。

2.LinkedBlockingQueue:LinkedBlockingQueue是基于鏈表實(shí)現(xiàn)的,和ArrayBlockingQueue不同的是,大小可以初始化設(shè)置,如果不設(shè)置,默認(rèn)設(shè)置大小為Integer.MAX_VALUE,LinkedBlockingQueue有兩個鎖對象,可以并行處理。

3.DelayQueue:DelayQueue是基于優(yōu)先級的一個無界隊(duì)列,隊(duì)列元素必須實(shí)現(xiàn)Delayed接口,支持延遲獲取,元素按照時間排序,只有元素到期后,消費(fèi)者才能從隊(duì)列中取出。

4.PriorityBlockingQueue:PriorityBlockingQueue是基于優(yōu)先級的一個無界隊(duì)列,底層是基于數(shù)組存儲元素的,元素按照優(yōu)選級順序存儲,優(yōu)先級是通過Comparable的compareTo方法來實(shí)現(xiàn)的(自然排序),和其他堵塞隊(duì)列不同的是,其只會堵塞消費(fèi)者,不會堵塞生產(chǎn)者,數(shù)組會不斷擴(kuò)容,這就是一個彩蛋,使用時要謹(jǐn)慎。

5.SynchronousQueue:SynchronousQueue是一個特殊的隊(duì)列,其內(nèi)部是沒有容器的,所以生產(chǎn)者生產(chǎn)一個數(shù)據(jù),就堵塞了,必須等消費(fèi)者消費(fèi)后,生產(chǎn)者才能再次生產(chǎn),稱其為隊(duì)列有點(diǎn)不合適,現(xiàn)實(shí)生活中,多個人才能稱為隊(duì),一個人稱為隊(duì)有些說不過去。

5.手寫堵塞隊(duì)列

我是參照了ArrayBlockingQueue的源碼寫的,歡迎大家斧正。

/**
 * @author yz
 * @version 1.0
 * @date 2020/10/31 11:24
 */
public class YzBlockingQuery {

    private Object[] tab; //隊(duì)列容器

    private int takeIndex; //出隊(duì)下標(biāo)

    private int putIndex; //入隊(duì)下標(biāo)

    private int size;//元素?cái)?shù)量

    private ReentrantLock reentrantLock = new ReentrantLock();

    private Condition notEmpty;//讀條件

    private Condition notFull;//寫條件

    public YzBlockingQuery(int tabCount) {
        if (tabCount <= 0) {
            new NullPointerException();
        }

        tab = new Object[tabCount];
        notEmpty = reentrantLock.newCondition();
        notFull = reentrantLock.newCondition();
    }

    public boolean offer(Object obj) {
        if (obj == null) { throw new NullPointerException(); }
        try {
            //獲取鎖
            reentrantLock.lock();
            //隊(duì)列已滿
            while (size==tab.length){
                System.out.println("隊(duì)列已滿");
                //堵塞
                notFull.await();
            }
            tab[putIndex]=obj;
            if(++putIndex==tab.length){
                putIndex=0;
            }
            size++;
            //喚醒讀線程
            notEmpty.signal();
            return true;
        } catch (Exception e) {
            //喚醒讀線程
            notEmpty.signal();
        } finally {
            reentrantLock.unlock();
        }
        return false;
    }

    public Object take(){
        try {
            reentrantLock.lock();
            while (size==0){
                System.out.println("隊(duì)列空了");
                //堵塞
                notEmpty.await();
            }
            Object obj= tab[takeIndex];
            //如果到了最后一個,則從頭開始
            if(++takeIndex==tab.length){
                takeIndex=0;
            }
            size--;
            //喚醒寫線程
            notFull.signal();
            return obj;
        }catch (Exception e){
            //喚醒寫線程
            notFull.signal();
        }finally {
            reentrantLock.unlock();
        }
        return null;
    }

    public static void main(String[] args) {
        Random random = new Random(100);
        YzBlockingQuery yzBlockingQuery=new YzBlockingQuery(5);
        Thread thread1 = new Thread(() -> {
            for (int i=0;i<100;i++) {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                yzBlockingQuery.offer(i);
                System.out.println("生產(chǎn)者生產(chǎn)了:"+i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i=0;i<100;i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Object take = yzBlockingQuery.take();
                System.out.println("消費(fèi)者消費(fèi)了:"+take);
            }
        });

        thread1.start();
        thread2.start();
    }
}

END

引用鏈接:blog.csdn.net/qq_38306425/article/details/109332045

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容