Java之BlockingQueue

再看BlockingQueue之前,首先得了解什么是Queue,以解了解Queue的逻辑结构还有特性,了解ReentrantLock可重入锁是什么。
BlockingQueue是阻塞队列,多应用于多线程开发线程池

这里讲解基本的操作,如添加、删除、返回队首元素、清除队列

方式

抛出异常

有返回值,不抛出异常

阻塞等待

超时等待

添加

add()

offer()

pull()

offer()

移除

remove()

poll()

take()

poll()

检测队首元素

element()

peak()

第一种add() remove() element() 继承自AbstractQueue.java
/**
     * 抛出异常  add remove
     */
    public static void test1(){

        //队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);//3代表容量
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));

        //IllegalStateException: Queue full 抛出异常!
         System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.element());//查看队首元素是谁
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //NoSuchElementException  抛出异常!
    }

首先看一下ArrayBlockingQueue()的源码,会发现ArrayBlockingQueue()源码在ArrayBlockingQueue.java下

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

继续点开this

//ArrayBlockingQueue.java
    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();
    }

这就是底层的代码,应该都看得懂吧,多看看源码,还是非常有帮助的

add()方法

点开源码我们发现add()方法来自于ArrayBlockingQueue.java

//ArrayBlockingQueue.java
    public boolean add(E e) {
        return super.add(e);
    }

继续点super后面的add(e),会发现ArrayBlockingQueue.java里面的add()方法继承于AbstractQueue.java

//AbstractQueue.java
    public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

看清楚,这里注意,当队列满的时候,会抛出IllegalStateException(“Queue full”)异常
还有源码中,add()方法的实现基于offer()方法。

remove()方法

同理看一下源码

//AbstractQueue.java
    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

当队列为空时,继续移除会抛出NoSuchElementException()异常,remove()方法实现基于poll()方法

element()方法
//AbstractQueue.java
    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

有关于offer()、poll()、peek()方法下面会讲解到

第二种offer()、poll()、peek()方法
/**
     * 有返回值,没有异常   offer  poll
     */
    public static void test2(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(2);

        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        //System.out.println(blockingQueue.offer("c"));

        System.out.println(blockingQueue.peek());
        System.out.println(blockingQueue.poll());//false 不抛出异常!
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //System.out.println(blockingQueue.poll());//null 返回null
    }

直接看方法的源码吧,方便理解

offer()方法
//ArrayBlockingQueue.java
    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();
        }
    }

可以看到这里加入了可重入锁,保证了线程安全
如果对队列如何存不了解的话,可以点开enqueue(e)

//ArrayBlockingQueue.java
    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();
    }

自己理解一下

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

dequeue()队列取源码

//ArrayBlockingQueue.java
    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;
    }
peek()方法
//ArrayBlockingQueue.java
    public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return itemAt(takeIndex); // null when queue is empty
        } finally {
            lock.unlock();
        }
    }
第三种offer(E e, long timeout, TimeUnit unit),poll(long timeout, TimeUnit unit)
/**
     * 等待,阻塞(等待超时)
     */

    public  static void test3() throws InterruptedException{
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        blockingQueue.offer("d",2, TimeUnit.SECONDS);


        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(3, TimeUnit.SECONDS));
    }

这里直接上源码,就是多了参数

//ArrayBlockingQueue.java
    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();
        }
    }
//ArrayBlockingQueue.java
    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();
        }
    }

这里可能对TimeUnit不了解,以及toNanos方法,这里讲一下

//TimeUnit.java部分代码
public enum TimeUnit {
    /**
     * Time unit representing one thousandth of a microsecond
     */
    NANOSECONDS {
        public long toNanos(long d)   { return d; }
        public long toMicros(long d)  { return d/(C1/C0); }
        public long toMillis(long d)  { return d/(C2/C0); }
        public long toSeconds(long d) { return d/(C3/C0); }
        public long toMinutes(long d) { return d/(C4/C0); }
        public long toHours(long d)   { return d/(C5/C0); }
        public long toDays(long d)    { return d/(C6/C0); }
        public long convert(long d, TimeUnit u) { return u.toNanos(d); }
        int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
    },

这里可以看到toNanos方法,可能还是不太了解但是翻到TimeUnit.java的最后会看到这么一个方法

//TimeUnit.java
    public void sleep(long timeout) throws InterruptedException {
        if (timeout > 0) {
            long ms = toMillis(timeout);
            int ns = excessNanos(timeout, ms);
            Thread.sleep(ms, ns);
        }
    }

是不是很熟悉,如果知道Thread.sleep(),方法的就会发现,这里怎么也有sleep()方法,其实是Thread.sleep()方法的包装,这里将月、日、时、分、秒封装成方法,提供了更强的可读性,建议使用TimeUnit

第四种pu()t take()
/**
     * 等待,阻塞(一直阻塞) put take
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);

        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put("d");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
    }

直接上源码,对照offer()和poll()看

//ArrayBlockingQueue.java
    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();
        }
    }
//ArrayBlockingQueue.java
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

请大家注意对比一下两个的返回值
有什么问题的话,请大家赐教