阻塞隊(duì)列ArrayBlockingQueue原理簡(jiǎn)析

ArrayBlockingQueue屬性與構(gòu)造方法

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    final Object[] items;

    int takeIndex;

    int putIndex;

    int count;

    final ReentrantLock lock;

    private final Condition notEmpty;

    private final Condition notFull;
    ...
}

ArrayBlockingQueue內(nèi)部是由Object[]數(shù)組實(shí)現(xiàn)的待秃。

takeIndex為隊(duì)列取出位置指針驻谆,putIndex為隊(duì)列插入位置指針。

同步操作依賴于ReentrantLock實(shí)現(xiàn)所踊,notEmpty為非空條件,notFull為非滿條件。

    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

可以通過構(gòu)造方法的參數(shù)capacity指定數(shù)組長(zhǎng)度读处,參數(shù)fair指定ReentrantLock的公平還是非公平模式,即是否允許后來的操作插隊(duì)與剛喚醒的線程競(jìng)爭(zhēng)鎖唱矛。

    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

第三個(gè)參數(shù) Collection<? extends E> c可以將現(xiàn)有集合插入ArrayBlockingQueue罚舱,如果集合的長(zhǎng)度大于參數(shù)capacity俊戳,會(huì)拋出ArrayIndexOutOfBoundsException。

阻塞插入 put

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock; //鎖
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

與LinkedBlockingQueue不同馆匿,ArrayBlockingQueue的插入與取出都是用同一個(gè)鎖抑胎,所以無法在插入的同時(shí)取出元素,吞吐量弱于LinkedBlockingQueue渐北。

當(dāng)隊(duì)列長(zhǎng)度達(dá)到上限阿逃,觸發(fā)notFull條件讓當(dāng)前線程掛起等待。當(dāng)notFull條件滿足赃蛛,執(zhí)行enqueue方法插入元素:

    private void enqueue(E x) {
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

putIndex即元素插入隊(duì)列的位置恃锉,當(dāng)元素插入后 putIndex自增如果等于隊(duì)列長(zhǎng)度上限,表示putIndex指針已越界呕臂,將putIndex置于0破托,這樣就能保證重復(fù)利用數(shù)組,而不是通過創(chuàng)建新數(shù)組來擴(kuò)容歧蒋。

然后將count自增1土砂,表示隊(duì)列元素增加一個(gè),最后通過notEmpty條件喚醒掛起等待消費(fèi)元素的線程谜洽。

阻塞取出 take

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

取出操作使用與插入操作相同的ReentrantLock保證同步塊的安全萝映,當(dāng)隊(duì)列長(zhǎng)度為0,觸發(fā)notEmpty條件讓線程掛起等待阐虚。當(dāng)隊(duì)列非空序臂,執(zhí)行dequeue方法取出元素:

    private E dequeue() {
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

取出元素的原理同插入是相同的,首先從takeIndex標(biāo)記的位置取出元素实束,接著將takeIndex自增奥秆,如果takeIndex此時(shí)等于隊(duì)列長(zhǎng)度上限,就將其置0從頭開始咸灿。

接著將count自減1构订,表示隊(duì)列的元素減少一個(gè)。

itrs 變量表示隊(duì)列的iterator析显,如果隊(duì)列元素減少一個(gè)通過elementDequeued方法同步更新iterator遍歷器保證線程安全鲫咽。

最后通過notFull條件喚醒一個(gè)等待非滿條件的線程執(zhí)行插入。

非阻塞插入

    public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

與阻塞插入的區(qū)別是谷异,當(dāng)隊(duì)列元素?cái)?shù)量已滿分尸,直接返回false。

非阻塞取出

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }

與阻塞取出的區(qū)別是歹嘹,當(dāng)隊(duì)列沒有元素箩绍,直接返回null。

不超時(shí)阻塞插入

    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {
        checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(e);
            return true;
        } finally {
            lock.unlock();
        }
    }

與阻塞插入的區(qū)別是尺上,等待notFull條件并非永久的材蛛,當(dāng)過了timeout長(zhǎng)度的時(shí)間后如果隊(duì)列還是滿的圆到,直接返回false。

不超時(shí)阻塞取出

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

與阻塞取出的區(qū)別是卑吭,等待notEmpty條件并非永久的芽淡,當(dāng)過了timeout長(zhǎng)度的時(shí)間后如果隊(duì)列還是空的,直接返回null豆赏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挣菲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掷邦,更是在濱河造成了極大的恐慌白胀,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抚岗,死亡現(xiàn)場(chǎng)離奇詭異或杠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宣蔚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門向抢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人件已,你說我怎么就攤上這事笋额。” “怎么了篷扩?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)茉盏。 經(jīng)常有香客問我鉴未,道長(zhǎng),這世上最難降的妖魔是什么鸠姨? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任铜秆,我火速辦了婚禮,結(jié)果婚禮上讶迁,老公的妹妹穿的比我還像新娘连茧。我一直安慰自己,他們只是感情好巍糯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布啸驯。 她就那樣靜靜地躺著,像睡著了一般祟峦。 火紅的嫁衣襯著肌膚如雪罚斗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天宅楞,我揣著相機(jī)與錄音针姿,去河邊找鬼袱吆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛距淫,可吹牛的內(nèi)容都是我干的绞绒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼榕暇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蓬衡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拐揭,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤撤蟆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后堂污,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體家肯,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年盟猖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讨衣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡式镐,死狀恐怖反镇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情娘汞,我是刑警寧澤歹茶,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站你弦,受9級(jí)特大地震影響惊豺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜禽作,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一尸昧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旷偿,春花似錦烹俗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尘喝,卻和暖如春磁浇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朽褪。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工置吓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留无虚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓衍锚,卻偏偏與公主長(zhǎng)得像友题,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子戴质,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一度宦、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)。 NEW:這種情況指的是告匠,通過 New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,659評(píng)論 0 44
  • 前言 分析LinkedBlockingQueue的實(shí)現(xiàn)原理前戈抄,需要先了解ReentrantLock 和Atomic...
    Mars_M閱讀 3,425評(píng)論 0 0
  • 相關(guān)文章Java并發(fā)編程(一)線程定義、狀態(tài)和屬性 Java并發(fā)編程(二)同步Java并發(fā)編程(三)volatil...
    劉望舒閱讀 5,225評(píng)論 1 31
  • 今天后专,我家小新到妮妮家和她一起玩划鸽,還主動(dòng)拉妮妮的手。妮妮給他看手機(jī)里的視頻戚哎,他靠在妮妮的肩上一起看裸诽。看來我不用擔(dān)心...
    南瓜土豆餅閱讀 174評(píng)論 1 2
  • 是個(gè)很好很好的朋友想了剛剛開頭的小說型凳,結(jié)果現(xiàn)在突然想到了把它寫下去丈冬。只是不知道自己還有沒有那種毅力和勇氣一直寫到結(jié)...
    喵了個(gè)嗚哇閱讀 159評(píng)論 1 0