ArrayBlockingQueue-我們到底能走多遠系列(42)

來源:互聯網
上載者:User

標籤:

我們到底能走多遠系列(42)

扯淡:

  乘著有空,讀些juc的源碼學習下。後續把juc大致走一邊,反正以後肯定要再來。

主題:

BlockingQueue 是什麼A java.util.Queue that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element. 一個能阻塞的隊列在兩個操作隊列時的阻塞:  1,擷取隊列中元素時,隊列為空白,則阻塞,直到隊列中有元素。  2,存放一個元素時,隊列已滿,則阻塞,直到隊列中有空位置可以存放。BlockingQueue 作為介面規定了實現的規矩。下面是隊列核心的存取操作方法的4個種類:
  Throws exception Special value Blocks Times out
Insert add(e) offer(e) put(e) offer(e, time, unit)
Remove remove() poll() take() poll(time, unit)
Examine element() peek() not applicable not applicable
 根據上面表,在隊列滿或空時的策略分別包含了,拋出異常,返回boolean值,阻塞線程,阻塞到逾時。為什麼要這麼選擇,就不清楚了。我們需要注意的是除了第三種,其他方法都沒有真正阻塞線程。  ArrayBlockingQueue:內部用數組實現的一個queue,按照元素先進先出(FIFO)原則。初始化後,隊列容量不可改變。支援可選的公平機制,來保證阻塞的操作線程能按照順序排列等待。預設是不公平機制。  源碼實現:1,使用Object[]的一個數組來儲存元素
// 隊列存放元素的容器final Object[] items;// 下一次讀取或移除的位置int takeIndex;// 存放下一個放入元素的位置int putIndex;// 隊列裡有效元素的數量int count;// 所有訪問的保護鎖final ReentrantLock lock;// 等待擷取的條件private final Condition notEmpty;// 等待放入的條件private final Condition notFull;

2,整個隊列是有一個環繞機制的,比如這時候我一直取資料,那麼讀取的下標會一直後移,知道數組的末尾。如果這時候制定數組的尾部後一個下標時數組的頭位。如此即實現環繞的一個隊列。如此實現十分精妙,可說是整個隊列實現的基礎機制。

如此,這個隊列的容量是不可改變的。

// 指標前移final int inc(int i) {    return (++i == items.length) ? 0 : i;}// 指標後移final int dec(int i) {    return ((i == 0) ? items.length : i) - 1;}

3,直接看下核心的put和take方法實現:

put

    public void put(E e) throws InterruptedException {        checkNotNull(e);//不能放null        final ReentrantLock lock = this.lock;//先把鎖賦給final修飾的局部變數        // 在JUC的很多類裡,都會看到這種寫法:把類的屬性賦值給方法內的用final修飾一個變數。        // 這是因為類的屬性是存放在堆裡的,方法內的變數是存放在方法棧上的,存取方法棧比訪問堆要快。        // 在這裡,this.lock屬性要訪問兩次,通過賦值給方法的局部變數,就節省了一次堆的訪問。        // 其他的類屬性只訪問一次就不需要這樣處理了。        lock.lockInterruptibly();//加鎖        try {            //迴圈保證避免避免虛假喚醒,虛假喚醒就是此事如果有多個線程都wait,
       //而被同時喚醒時都會去執行下面的insert //如果在while迴圈中,那麼喚醒後先判斷count大小,來確定是繼續wait還是insert。 while (count == items.length) notFull.await();//阻塞線程 insert(e); } finally { lock.unlock();//釋放鎖 } }

 take

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

其中使用到insert和extract方法,當然也可以看到只有持有鎖的情況下才會調用這兩個方法,如此這個方法的調用不需要關係是否安全執行緒,調用前保證安全執行緒:

    private void insert(E x) {        items[putIndex] = x;// 1,存值,非常簡便        putIndex = inc(putIndex);//2,移動下標,使用inc方法        ++count;//3,增加元素總數        notEmpty.signal();//4,通知在非空條件上等待的讀線程     }
    private E extract() {        final Object[] items = this.items;//先將類變數賦給方法變數,前面提過這個用處        E x = this.<E>cast(items[takeIndex]);        items[takeIndex] = null;        takeIndex = inc(takeIndex);        --count;        notFull.signal();        return x;    }

 

操作:

1,一個環的數組

 

2,再放一個元素:

 

 

3,取一個元素

 

 

當然ArrayBlockingQueue裡還有其他方法,這裡就不贅述了。有興趣的同學可以深入繼續探索。

 

總結:

 1,一個環的數組設計十分巧妙。

 2,將類變數賦給方法變數的編碼方式

 

 

讓我們繼續前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不會成功。

ArrayBlockingQueue-我們到底能走多遠系列(42)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.