這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)深入淺析java并發(fā)中的ArrayBlockingQueue,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)專(zhuān)注于企業(yè)營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、網(wǎng)站重做改版、平遙網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5高端網(wǎng)站建設(shè)、成都做商城網(wǎng)站、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為平遙等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
java并發(fā)之ArrayBlockingQueue詳細(xì)介紹
ArrayBlockingQueue是常用的線程集合,在線程池中也常常被當(dāng)做任務(wù)隊(duì)列來(lái)使用。使用頻率特別高。他是維護(hù)的是一個(gè)循環(huán)隊(duì)列(基于數(shù)組實(shí)現(xiàn)),循環(huán)結(jié)構(gòu)在數(shù)據(jù)結(jié)構(gòu)中比較常見(jiàn),但是在源碼實(shí)現(xiàn)中還是比較少見(jiàn)的。
線程安全的實(shí)現(xiàn)
線程安全隊(duì)列,基本是離不開(kāi)鎖的。ArrayBlockingQueue使用的是ReentrantLock,配合兩種Condition,實(shí)現(xiàn)了集合的線程安全操作。這里稍微說(shuō)一個(gè)好習(xí)慣,下面是成員變量的聲明。
private static final long serialVersionUID = -817911632652898426L; final Object[] items; int takeIndex; int putIndex; int count; final ReentrantLock lock; private final Condition notEmpty; private final Condition notFull; transient Itrs itrs = null;
賦值的操作基本都是在構(gòu)造函數(shù)里做的。這樣有個(gè)好處,代碼執(zhí)行可控。成員變量的初始化也是會(huì)合并在構(gòu)造方法里執(zhí)行的,但是在執(zhí)行順序上需要好好斟酌,如果寫(xiě)在構(gòu)造方法里初始化,則沒(méi)有相關(guān)問(wèn)題。
阻塞隊(duì)列的常用場(chǎng)所就是生產(chǎn)者消費(fèi)者。一般都是生產(chǎn)者放入,消費(fèi)者從頭取數(shù)據(jù)。下面重點(diǎn)說(shuō)這兩個(gè)操作。
這兩個(gè)操作都是依靠鎖來(lái)保證線程安全的。
生產(chǎn)操作
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(); } }
put等放入操作,首先是獲取鎖,如果發(fā)現(xiàn)數(shù)據(jù)滿了,就通過(guò)notFull的condition,來(lái)阻塞線程。這里的條件判定一定是用while而不是if,多線程情況下,可以被喚醒后發(fā)現(xiàn)又滿了。
private void enqueue(E x) { final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); }
這個(gè)是入隊(duì)列的操作。首先獲取維護(hù)的數(shù)組。putindex就是放入操作的標(biāo)志。這個(gè)操作會(huì)一直加。達(dá)到預(yù)定的長(zhǎng)度后就變成0從頭開(kāi)始計(jì)數(shù)。這樣插入的操作就是一個(gè)循環(huán)的操作了,count就是用來(lái)做計(jì)數(shù)的,作為能否插入數(shù)據(jù)的一個(gè)標(biāo)準(zhǔn),插入數(shù)據(jù)后就通過(guò)notEmpty的condition發(fā)出一個(gè)信號(hào)喚醒消費(fèi)線程。
消費(fèi)操作
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } finally { lock.unlock(); } }
消費(fèi)的方法也是這樣。先獲取鎖,然后進(jìn)行條件判斷,如果沒(méi)有數(shù)據(jù),則阻塞線程。注意點(diǎn)和put一樣。
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; }
取數(shù)據(jù)的時(shí)候,也依靠takeIndex,這是一個(gè)標(biāo)志,這個(gè)數(shù)值也會(huì)一直增加,表示取的第一個(gè)數(shù)據(jù)的位置。如果這個(gè)標(biāo)志走到最后,然后變成0,從頭再來(lái)。這樣保證取出的數(shù)據(jù)都是fifo的順序。刪除的時(shí)候如果發(fā)現(xiàn)迭代中,則會(huì)修改迭代器的遍歷。然后通過(guò)notFull的condition來(lái)喚醒生產(chǎn)線程。
移除操作
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) { final int putIndex = this.putIndex; int i = takeIndex; do { if (o.equals(items[i])) { removeAt(i); return true; } if (++i == items.length) i = 0; } while (i != putIndex); } return false; } finally { lock.unlock(); } }
對(duì)于remove操作就比較麻煩了,首先獲取鎖之后,把兩個(gè)標(biāo)志位本地化,然后找到要?jiǎng)h除的元素的位置。調(diào)用removeAt,這里刪除需要對(duì)標(biāo)志位做改變。
void removeAt(final int removeIndex) { final Object[] items = this.items; if (removeIndex == takeIndex) { items[takeIndex] = null; if (++takeIndex == items.length) takeIndex = 0; count--; if (itrs != null) itrs.elementDequeued(); } else { final int putIndex = this.putIndex; for (int i = removeIndex;;) { int next = i + 1; if (next == items.length) next = 0; if (next != putIndex) { items[i] = items[next]; i = next; } else { items[i] = null; this.putIndex = i; break; } } count--; if (itrs != null) itrs.removedAt(removeIndex); } notFull.signal(); }
如果刪除的元素是位置和takeindex一樣。那就可以直接刪除,然后讓刪除標(biāo)志位向后移動(dòng)。如果不是,則從刪除的位置開(kāi)始,進(jìn)行后面向前面的數(shù)據(jù)覆蓋的操作。直到遇到putindex的前一個(gè)位置。然后把那個(gè)位置的數(shù)據(jù)設(shè)置為null。并且把putindex的位置往前移動(dòng)一格,正在迭代的時(shí)候要?jiǎng)h除數(shù)據(jù)并且喚醒生產(chǎn)線程。
上述就是小編為大家分享的深入淺析java并發(fā)中的ArrayBlockingQueue了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
新聞標(biāo)題:深入淺析java并發(fā)中的ArrayBlockingQueue
瀏覽地址:http://www.chinadenli.net/article38/joigsp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站排名、ChatGPT、網(wǎng)站設(shè)計(jì)、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)