數組阻塞隊列 ArrayBlockingQueue

前言


ArrayBlockingQueue這個是數組阻塞隊列,是BlockingQueue接口的一個實現類,它是一個容量有限的隊列,一旦隊列大小確認了就不能再改變了,使用方法,下面介紹。

使用


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Main1 {
    public static void main(String[] args) {
        // 準備好數組阻塞隊列
        BlockingQueue<Integer> bq = new ArrayBlockingQueue<Integer>(1024);
        // 兩個Runnable實現類
        Producer producer = new Producer(bq);
        Consumer consumer = new Consumer(bq);
        Thread t1 = new Thread(producer);
        Thread t2 = new Thread(consumer);

        // 線程開啓
        t1.start();
        t2.start();


    }
}

class Producer implements Runnable {
    private BlockingQueue<Integer> blockingQueue = null;

    public Producer() {
    }

    public Producer(BlockingQueue<Integer> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        // try catch主要是put(null),會拋出Exception
        try {
            // 爲了實現一個生產慢,消費快,消費阻塞的效果
            Thread.sleep(1000);
            blockingQueue.put(1);
            Thread.sleep(1000);
            blockingQueue.put(4);
            Thread.sleep(1000);
            blockingQueue.put(555);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    private BlockingQueue<Integer> blockingQueue = null;

    public Consumer() {
    }

    public Consumer(BlockingQueue<Integer> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        try {
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

源碼解析

其實這個ArrayBlockingQueue的源碼並不難,底層就是一個數組在進行操作,我們通過上面的那個Demo代碼,進行debug,然後來探究一下到底底層是如何操作的。

// 實例化就不多說了
    /**
     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
     * capacity and default access policy.
     *
     * @param capacity the capacity of this queue 隊列的容量
     * @throws IllegalArgumentException if {@code capacity < 1}
     */
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

    public ArrayBlockingQueue(int capacity, boolean fair) {
        // 判斷傳進來設置的隊列容量是不是正整數
        if (capacity <= 0)
            // 不是肯定要拋出異常,你玩兒我?
            throw new IllegalArgumentException();
        //  final Object[] items; 這個items就是個Object類型的數組
        this.items = new Object[capacity];
        // 這個鎖肯定是要用的,用的是ReentrantLock
        lock = new ReentrantLock(fair);
        // 兩個監視器用於隊列滿和隊列用阻塞之時使用。
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }   

    // 當生產者調用put方法時

    public void put(E e) throws InterruptedException {
        // 查看放進隊列的對象是不是null,是則拋出NPE
        checkNotNull(e);
        // 拿到前面初始化時候的那把鎖
        final ReentrantLock lock = this.lock;
        // 加鎖,其他線程沒法用下面的方法了
        lock.lockInterruptibly();
        try {
            // 看看這個隊列裏面有多少個數,是不是比容量大了
            while (count == items.length)
                // 隊列滿了,就等待
                notFull.await();
            // 沒滿就入列
            enqueue(e);
        } finally {
            // 解鎖操作不能忘
            lock.unlock();
        }
    }

    // 入列操作

    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        // 告訴阻塞的隊列空監視器,現在不空了。
        notEmpty.signal();
    }

    // take操作

    public E take() throws InterruptedException {
        // 拿鎖
        final ReentrantLock lock = this.lock;
        // 加鎖
        lock.lockInterruptibly();
        try {
            // 如果隊列裏面沒東西
            while (count == 0)
                // 阻塞
                notEmpty.await();
            // 有東西,就返回一個對象
            return dequeue();
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }

    // 出列操作

    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        // 拿到這個所謂的隊列
        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;
    }

後記


所以,其實我們後面大部分業務場景用的就是用這個數組阻塞隊列,不過,既然是學習的話,當然是其他實現類也是要看看了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章