一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

BlockingQueue主要屬性和構(gòu)造函數(shù)

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-13 11:36 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

今天我們來聊一聊以數(shù)組為數(shù)據(jù)結(jié)構(gòu)的阻塞隊(duì)列 ArrayBlockingQueue,它實(shí)現(xiàn)了 BlockingQueue 接口,繼承了抽象類 AbstractQueue。

BlockingQueue 提供了三個(gè)元素入隊(duì)的方法。

boolean add(E e);

boolean offer(E e);

void put(E e) throws InterruptedException;

三個(gè)元素出隊(duì)的方法。

E take() throws InterruptedException;

E poll(long timeout, TimeUnit unit)
        throws InterruptedException;

boolean remove(Object o);

一起來看看,ArrayBlockingQueue 是如何實(shí)現(xiàn)的吧。

初識(shí)

首先看一下 ArrayBlockingQueue 的主要屬性和構(gòu)造函數(shù)。

屬性

//存放元素
final Object[] items; 

//取元素的索引
int takeIndex;

//存元素的索引
int putIndex;

//元素的數(shù)量
int count;

//控制并發(fā)的鎖
final ReentrantLock lock;

//非空條件信號(hào)
private final Condition notEmpty;

//非滿條件信號(hào)量
private final Condition notFull;

transient Itrs itrs = null;

從以上屬性可以看出:

  1. 以數(shù)組的方式存放元素。
  2. 用 putIndex 和 takeIndex 控制元素入隊(duì)和出隊(duì)的索引。
  3. 用重入鎖控制并發(fā)、保證線程的安全。

構(gòu)造函數(shù)

ArrayBlockingQueue 有三個(gè)構(gòu)造函數(shù),其中 public ArrayBlockingQueue(int capacity, boolean fair, Collection c) 構(gòu)造函數(shù)并不常用,暫且不提。看其中兩個(gè)構(gòu)造函數(shù)。

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

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    //構(gòu)造數(shù)組
    this.items = new Object[capacity];
    //默認(rèn)以非公平鎖初始化 ReentrantLock
    lock = new ReentrantLock(fair);
    //創(chuàng)建兩個(gè)條件信號(hào)量
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

可以看出 ArrayBlockingQueue 必須再創(chuàng)建時(shí)傳入數(shù)組的大小。

元素入隊(duì)

ArrayBlockingQueue 有 add()、offer()、put()、offer(E e, long timeout, TimeUnit unit) 方法用來元素的入隊(duì)。

add
//ArrayBlockingQueue.add()
public boolean add(E e) {
    //調(diào)用父類的 AbstractQueue.add() 方法
    return super.add(e);
}

//AbstractQueue.add()
public boolean add(E e) {
    //調(diào)用 ArrayBlockingQueue.offer(),成功則返回 true,否則拋出異常
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

//ArrayBlockingQueue.offer()
public boolean offer(E e) {
    //非空檢查
    checkNotNull(e);
    //加鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //數(shù)組滿了,返回 false
        if (count == items.length)
            return false;
        else {
            //添加元素
            enqueue(e);
            return true;
        }
    } finally {
        //解鎖
        lock.unlock();
    }
}

//ArrayBlockingQueue.enqueue()
private void enqueue(E x) {
    final Object[] items = this.items;
    //直接放到 putIndex 的位置
    items[putIndex] = x;
    //如果索引滿了,putIndex 就從 0 開始,為什么呢?
    if (++putIndex == items.length)
        putIndex = 0;
    //數(shù)量加一
    count++;
    //數(shù)組里面有數(shù)據(jù)了,對(duì) notEmpty 條件隊(duì)列進(jìn)行通知
    notEmpty.signal();
}

上面留下了一個(gè)坑,索引等于數(shù)組的長(zhǎng)度的時(shí)候,索引就從 0 開始了。其實(shí)很簡(jiǎn)單,這個(gè)數(shù)組是不是先入先出的,0 索引的數(shù)組先入隊(duì),也是先出隊(duì)的。這時(shí)候 0 索引的位置就空了,所以 putIndex 到達(dá)數(shù)組長(zhǎng)度的時(shí)候就可以從 0 開始。這里可以看出,ArrayBlockingQueue 是絕對(duì)不可以修改數(shù)組長(zhǎng)度的,一旦初始化后長(zhǎng)度就不能再改變了。

put
//ArrayBlockingQueue.put()
public void put(E e) throws InterruptedException {
    //非空檢查
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    //加鎖
    lock.lockInterruptibly();
    try {
        //數(shù)組滿了,線程加入 notFull 隊(duì)列中等待被喚醒
        while (count == items.length)
            notFull.await();
        //添加元素
        enqueue(e);
    } finally {
        //解鎖
        lock.unlock();
    }
}
offer

ArrayBlockingQueue 中有兩個(gè) offer() 方法,offer(E e) 和 offer(E e, long timeout, TimeUnit unit),add() 方法調(diào)用的就是 offer(E e) 方法。

//ArrayBlockingQueue.offer(E e, long timeout, TimeUnit unit)
public boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    //非空檢查
    checkNotNull(e);
    //將時(shí)間轉(zhuǎn)換為納秒
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    //加鎖
    lock.lockInterruptibly();
    try {
        //當(dāng)數(shù)組滿了
        while (count == items.length) {
            //時(shí)間到了,元素還沒有入隊(duì),則返回 false
            if (nanos <= 0)
                return false; 
            //線程加入 notFull 隊(duì)列中,等待被喚醒,到達(dá) nanos 時(shí)間返回剩余的 nanos 時(shí)間
            nanos = notFull.awaitNanos(nanos);
        }
        //元素入隊(duì)
        enqueue(e);
        return true;
    } finally {
        //解鎖
        lock.unlock();
    }
}

以上就是所有的元素入隊(duì)的方法,可以得出一些結(jié)論:

  1. add() 元素滿了,就拋出異常。
  2. offer() 元素滿了,返回 false。
  3. put() 元素滿了,線程阻塞等待被入隊(duì)。
  4. offer(E e, long timeout, TimeUnit unit) 加入超時(shí)時(shí)間,如果時(shí)間到了元素還是沒有被入隊(duì),則返回 false

移除元素

ArrayBlockingQueue 提供了 poll()、take()、poll(long timeout, TimeUnit unit)、remove() 方法用于元素的出隊(duì)。

poll

ArrayBlockingQueue 中有兩個(gè) poll() 方法,poll() 和 poll(long timeout, TimeUnit unit)。

//ArrayBlockingQueue.poll()
public E poll() {
    final ReentrantLock lock = this.lock;
    //加鎖
    lock.lock();
    try {
        //沒有元素返回 null,否則元素出隊(duì)
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

//ArrayBlockingQueue.dequeue()
private E dequeue() {
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    //獲取 takeIndex 上的元素
    E x = (E) items[takeIndex];
    //設(shè)置 takeIndex 索引上的元素為 null
    items[takeIndex] = null;
    //當(dāng) takeIndex 長(zhǎng)度是數(shù)組長(zhǎng)度,takeIndex 索引從 0 開始
    if (++takeIndex == items.length)
        takeIndex = 0;
    //元素?cái)?shù)量 -1
    count--;

    if (itrs != null)
        //更新迭代器
        itrs.elementDequeued();
    //喚醒 notFull 的等待隊(duì)列,其中等待的第一個(gè)線程可以添加元素了
    notFull.signal();
    return x;
}
//ArrayBlockingQueue.poll(long timeout, TimeUnit unit)
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    ////將時(shí)間轉(zhuǎn)換為納秒
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    //加鎖
    lock.lockInterruptibly();
    try {
        //數(shù)組為空,超時(shí)還沒有元素出隊(duì),則返回 null
        while (count == 0) {
            if (nanos <= 0)
                return null;
            //線程加入 notEmpty 中,等待被喚醒,到達(dá) nanos 時(shí)間返回剩余的 nanos 時(shí)間
            nanos = notEmpty.awaitNanos(nanos);
        }
        //元素出隊(duì)
        return dequeue();
    } finally {
        lock.unlock();
    }
}
take
//ArrayBlockingQueue.take()
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    //加鎖
    lock.lockInterruptibly();
    try {
        //無元素
        while (count == 0)
            //將線程加入 notEmpty 的等待隊(duì)列中,等待被入隊(duì)的元素喚醒
            notEmpty.await();
        //元素出隊(duì)
        return dequeue();
    } finally {
        //解鎖
        lock.unlock();
    }
}
remove
//ArrayBlockingQueue.remove()
public boolean remove(Object o) {
    //非空檢查
    if (o == null) return false;
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    //加鎖
    lock.lock();
    try {

        if (count > 0) {
            //入隊(duì)元素的索引
            final int putIndex = this.putIndex;
            //出隊(duì)元素的索引
            int i = takeIndex;
            do {
                //找到元素
                if (o.equals(items[i])) {
                    removeAt(i);
                    return true;
                }
                //i 等于數(shù)組長(zhǎng)度的時(shí)候,從 0 開始
                if (++i == items.length)
                    i = 0;
            // i == putIndex 說明已經(jīng)遍歷了一遍
            } while (i != putIndex);
        }
        return false;
    } finally {
        //解鎖
        lock.unlock();
    }
}

//ArrayBlockingQueue.removeAt()
void removeAt(final int removeIndex) {
    final Object[] items = this.items;
    //需要出隊(duì)的 removeIndex 正好是 takeIndex
    if (removeIndex == takeIndex) {
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        //更新迭代器
        if (itrs != null)
            itrs.elementDequeued();
    } else {
        final int putIndex = this.putIndex;
        // 循環(huán)移動(dòng)元素,將 next 元素向前移動(dòng) 1 個(gè)
        for (int i = removeIndex;;) {
            int next = i + 1;
            if (next == items.length)
                next = 0;
            if (next != putIndex) {
                items[i] = items[next];
                i = next;
            } else {
                //設(shè)置 i 索引的位置為空,putIndex 索引為 i
                items[i] = null;
                this.putIndex = i;
                break;
            }
        }
        count--;
        if (itrs != null)
            itrs.removedAt(removeIndex);
    }
    // 喚醒 notFull 隊(duì)列中等待的線程,通知可以元素入隊(duì)了
    notFull.signal();
}

以上就是所有的元素出隊(duì)的方法,可以得出一些結(jié)論:

  1. poll() 元素出隊(duì)為空,則返回空
  2. take() 元素出隊(duì)為空的時(shí)候,會(huì)阻塞線程
  3. remove() 元素出隊(duì)的時(shí)候可能會(huì)移動(dòng)數(shù)組
  4. poll(long timeout, TimeUnit unit) 加入超時(shí)時(shí)間,如果時(shí)間到了還是沒有元素需要出隊(duì),則返回 null

總結(jié)

ArrayBlockingQueue 可以被用在生產(chǎn)者和消費(fèi)者模型中。

  1. ArrayBlockingQueue,不能被擴(kuò)容,初始化被指定容量。
  2. 利用 putIndex 和 takeIndex 循環(huán)利用數(shù)組。
  3. 利用了 ReentrantLock 和 兩個(gè) Condition 保證了線程的安全。
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    9001

    瀏覽量

    153735
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4381

    瀏覽量

    64865
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    573

    瀏覽量

    40748
  • 數(shù)組
    +關(guān)注

    關(guān)注

    1

    文章

    420

    瀏覽量

    26542
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    SystemVerilog中的類構(gòu)造函數(shù)new

    在systemverilog中,如果一個(gè)類沒有顯式地聲明構(gòu)造函數(shù)(new()),那么編譯仿真工具會(huì)自動(dòng)提供一個(gè)隱式的new()函數(shù)。這個(gè)new函數(shù)會(huì)默認(rèn)地將所有
    發(fā)表于 11-16 09:58 ?3961次閱讀

    什么是構(gòu)造函數(shù)?怎樣去編寫構(gòu)造函數(shù)

    什么是構(gòu)造函數(shù)?怎樣去編寫構(gòu)造函數(shù)呢?
    發(fā)表于 02-22 08:31

    一個(gè)基于多屬性協(xié)商的效用函數(shù)研究

    傳統(tǒng)的效用函數(shù)模型還不能滿足多屬性協(xié)商中反映協(xié)商屬性間的關(guān)聯(lián)關(guān)系的內(nèi)在要求,束縛了自動(dòng)協(xié)商的應(yīng)用。本文闡述了自動(dòng)協(xié)商的相關(guān)知識(shí)背景,分析了多屬性協(xié)商的特征和協(xié)
    發(fā)表于 01-22 14:22 ?11次下載

    基于生成函數(shù)的格雷對(duì)分析與構(gòu)造

    該文由傳統(tǒng)的格雷對(duì)構(gòu)造方法交織和級(jí)聯(lián)出發(fā),提出了一種新的稱之為生成函數(shù)的格雷對(duì)構(gòu)造方法,該方法適用于長(zhǎng)度為2n 的格雷對(duì)。文中分析了格雷對(duì)生成函數(shù)和希爾維斯特Hadamard
    發(fā)表于 02-08 16:04 ?8次下載

    基于plateaued函數(shù)的平衡布爾函數(shù)構(gòu)造

    不相交plateaued函數(shù),一類特殊的布爾置換以及一個(gè)高非線性度平衡函數(shù),提出了一個(gè)構(gòu)造高非線性度平衡布爾函數(shù)的方法。通過分析可知,利用該方法可以
    發(fā)表于 12-17 09:43 ?0次下載

    如何深度解析C++拷貝構(gòu)造函數(shù)詳細(xì)資料說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是如何深度解析C++拷貝構(gòu)造函數(shù)詳細(xì)資料說明。
    發(fā)表于 07-05 17:41 ?0次下載
    如何深度解析C++拷貝<b class='flag-5'>構(gòu)造</b><b class='flag-5'>函數(shù)</b>詳細(xì)資料說明

    Linux共享庫的構(gòu)造函數(shù)和析構(gòu)函數(shù)

    共享庫有類似C++類構(gòu)造和析構(gòu)函數(shù)函數(shù),當(dāng)動(dòng)態(tài)庫加載和卸載的時(shí)候,函數(shù)會(huì)被分別執(zhí)行。一個(gè)函數(shù)加上 constructor的 attribu
    的頭像 發(fā)表于 06-22 09:18 ?2536次閱讀
    Linux共享庫的<b class='flag-5'>構(gòu)造</b><b class='flag-5'>函數(shù)</b>和析構(gòu)<b class='flag-5'>函數(shù)</b>

    類的拷貝構(gòu)造函數(shù)主要用途是什么?

    類在實(shí)例化的時(shí)候會(huì)調(diào)用類的缺省構(gòu)造函數(shù),在struct里,要定義一個(gè)同名函數(shù)指針指向一個(gè)具有構(gòu)造函數(shù)功能的初始化
    的頭像 發(fā)表于 06-24 14:28 ?5080次閱讀

    C++:詳談構(gòu)造函數(shù)

    構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對(duì)象的時(shí)候,由編譯器自動(dòng)調(diào)用,在對(duì)象的生命周期內(nèi)只且調(diào)用一次,以保證每個(gè)數(shù)據(jù)成員都有一個(gè)合適的初始值。
    的頭像 發(fā)表于 06-29 11:44 ?1982次閱讀
    C++:詳談<b class='flag-5'>構(gòu)造</b><b class='flag-5'>函數(shù)</b>

    C++:詳談拷貝構(gòu)造函數(shù)

    只有單個(gè)形參,而且該形參是對(duì)本類類型對(duì)象的引用(常用const修飾),這樣的構(gòu)造函數(shù)稱為拷貝構(gòu)造函數(shù)??截?b class='flag-5'>構(gòu)造
    的頭像 發(fā)表于 06-29 11:45 ?2314次閱讀
    C++:詳談拷貝<b class='flag-5'>構(gòu)造</b><b class='flag-5'>函數(shù)</b>

    C++之拷貝構(gòu)造函數(shù)的淺copy及深copy

    C++編譯器會(huì)默認(rèn)提供構(gòu)造函數(shù);無參構(gòu)造函數(shù)用于定義對(duì)象的默認(rèn)初始化狀態(tài);拷貝構(gòu)造函數(shù)在創(chuàng)建對(duì)象
    的頭像 發(fā)表于 12-24 15:31 ?1002次閱讀

    c++中構(gòu)造函數(shù)學(xué)習(xí)的總結(jié)(一)

    關(guān)于這個(gè)構(gòu)造函數(shù),簡(jiǎn)單理解就是在一個(gè)類中,有一個(gè)函數(shù),它的函數(shù)名稱和類名同名,而且這個(gè)構(gòu)造函數(shù)
    的頭像 發(fā)表于 12-24 18:06 ?991次閱讀

    基于布爾函數(shù)導(dǎo)數(shù)的布爾置換構(gòu)造

    布爾函數(shù)導(dǎo)數(shù)的性質(zhì)在密碼構(gòu)造中起著重要的作用。文中利用布爾函數(shù)導(dǎo)數(shù)的性質(zhì),構(gòu)造了一個(gè)新的平衡布爾函數(shù)然后基于平衡布爾
    發(fā)表于 06-17 10:58 ?15次下載

    2.10 學(xué)生類-構(gòu)造函數(shù) (15分)

    )。 ###1.編寫有參構(gòu)造函數(shù): 能對(duì)name,sex,age賦值。 ###2.覆蓋toString函數(shù):按照格式:類名 [name=, sex=, age=]輸出。使用idea自動(dòng)生成,然后在修改成該輸出格式 ###3.對(duì)每
    發(fā)表于 12-29 19:05 ?1次下載
    2.10 學(xué)生類-<b class='flag-5'>構(gòu)造</b><b class='flag-5'>函數(shù)</b> (15分)

    深入探索GCC的attribute屬性

    修飾變量、函數(shù)或者數(shù)據(jù)類型的屬性,屬性有很多,有些確實(shí)很有用。 找了幾個(gè)可以修飾函數(shù)屬性,供大家參考下。 如果希望
    的頭像 發(fā)表于 02-13 10:05 ?415次閱讀