BlockingQueue之ArrayBlockingQueue

BlockingQueue之ArrayBlockingQueue

一、基本概念

ArrayBlockingQueue的实现是依靠内部的数组,此数组在实例化时即分配了容量capacity,且不能扩大。
此类是线程安全的。方法都加了ReentrantLock。
此类实例在构造时,可以选择是否公平。(公平:FIFO,非公平:出队顺序不一定)

二、基本方法

  1. add(E element):boolean, 失败就抛出异常
  2. offer(E element):boolean, 成功:true, 失败:false
  3. put(E element):void throws Interrupted Exception, 阻塞
  4. take():E throws Interrupted Exception, 阻塞(取出队头并删除)
  5. poll():E (取出队头,并删除)
  6. peek:查看队头,不删除

三、构造函数

public ArrayBlockingQueue(int capacity) {

public ArrayBlockingQueue(int capacity, boolean fair) 

默认情况下,fair为false,当然也可以指定fair。出队的公平性在单线程里提现不出来,一定是多线程的情况下。

看一个例子

代码(一)fair

	//初始化
    public void initQueue(int capacity, boolean fair) {
        arrayBlockingQueue = new ArrayBlockingQueue(capacity, fair);
    }
    
    //输出
    public void printQueue() {
        System.out.println(arrayBlockingQueue);
    }
    
    //test
    public static void main(String[] args) {
        final ArrayBlockingQueueLearn arrayBlockingQueueLearn = new ArrayBlockingQueueLearn();

        arrayBlockingQueueLearn.initQueue(100, true);
        for (int t = 0; t < 5; t++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hi, i am " + Thread.currentThread().getName());
                    arrayBlockingQueueLearn.testAdd();
                }
            }, "thread:" + t);
            thread.start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        arrayBlockingQueueLearn.printQueue();
    }

结果
(1)fair = true;

hi, i am thread:1
hi, i am thread:0
hi, i am thread:2
hi, i am thread:3
hi, i am thread:4
[thread:1第0个元素, thread:1第1个元素, thread:1第2个元素, thread:1第3个元素, thread:1第4个元素, thread:1第5个元素, thread:1第6个元素, thread:1第7个元素, thread:1第8个元素, thread:1第9个元素, thread:0第0个元素, thread:0第1个元素, thread:0第2个元素, thread:0第3个元素, thread:0第4个元素, thread:0第5个元素, thread:0第6个元素, thread:0第7个元素, thread:0第8个元素, thread:0第9个元素, thread:2第0个元素, thread:2第1个元素, thread:2第2个元素, thread:2第3个元素, thread:2第4个元素, thread:2第5个元素, thread:2第6个元素, thread:2第7个元素, thread:2第8个元素, thread:2第9个元素, thread:3第0个元素, thread:3第1个元素, thread:3第2个元素, thread:3第3个元素, thread:3第4个元素, thread:3第5个元素, thread:3第6个元素, thread:3第7个元素, thread:3第8个元素, thread:3第9个元素, thread:4第0个元素, thread:4第1个元素, thread:4第2个元素, thread:4第3个元素, thread:4第4个元素, thread:4第5个元素, thread:4第6个元素, thread:4第7个元素, thread:4第8个元素, thread:4第9个元素]

(2)fair = false;


hi, i am thread:0
hi, i am thread:4
hi, i am thread:3
hi, i am thread:2
hi, i am thread:1
[thread:0第0个元素, thread:0第1个元素, thread:0第2个元素, thread:0第3个元素, thread:0第4个元素, thread:0第5个元素, thread:4第0个元素, thread:3第0个元素, thread:3第1个元素, thread:3第2个元素, thread:3第3个元素, thread:3第4个元素, thread:3第5个元素, thread:3第6个元素, thread:3第7个元素, thread:3第8个元素, thread:3第9个元素, thread:4第1个元素, thread:4第2个元素, thread:2第0个元素, thread:4第3个元素, thread:2第1个元素, thread:4第4个元素, thread:2第2个元素, thread:2第3个元素, thread:2第4个元素, thread:2第5个元素, thread:2第6个元素, thread:0第6个元素, thread:0第7个元素, thread:0第8个元素, thread:0第9个元素, thread:1第0个元素, thread:1第1个元素, thread:1第2个元素, thread:4第5个元素, thread:2第7个元素, thread:1第3个元素, thread:4第6个元素, thread:2第8个元素, thread:2第9个元素, thread:1第4个元素, thread:1第5个元素, thread:1第6个元素, thread:1第7个元素, thread:1第8个元素, thread:4第7个元素, thread:1第9个元素, thread:4第8个元素, thread:4第9个元素]

结论:比较两次结果,可以看出,当fair=true,那么队列的输出就是FIFO的节奏。当fair = false时,输出的顺序不是FIFO

四、put 和 take

put和take是阻塞式的方法,其源码如下

代码(二)put 和 take

	//put方法
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();//阻塞,并释放锁,一直等到信号notify
            insert(e);
        } finally {
            lock.unlock();
        }
    }
    
    //插入对尾
     private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();//通知notEmpty
    }
    
    //take方法
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();//阻塞,等待notEmtpy的notify
            return extract();
        } finally {
            lock.unlock();
        }
    }
    
    //取出队头
    private E extract() {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();//通知notFull
        return x;
    }
    

测试一下put的阻塞

代码(三) 测试put的阻塞

public void testPutAndTake() {
        //初始化队列
        initQueue(5, true);
        //put线程
        Thread putThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //i == 5时,阻塞
                for (int i = 1; i <= 6; i++)
                    try {
                        System.out.println("putThread, start putting 第" + i + "个元素");
                        arrayBlockingQueue.put(Thread.currentThread().getName() + "第" + i + "元素");
                        System.out.println("putThread, end putting 第" + i + "个元素");

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        }, "putThread");

        //take线程
        Thread takeThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("start taking element");
                    String firstElement = (String)arrayBlockingQueue.take();
                    System.out.println("finish taking element: " + firstElement);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "takeThread");

        //启动put线程,并在5s中后再启动take线程

        putThread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        takeThread.start();
    }

结果输出如下,可以看出put在插入最后一个元素时阻塞了,等待notFull信号的notify,于是,当take取走第一个元素后,put继续向队列插入第6个元素

putThread, start putting 第1个元素
putThread, end putting 第1个元素
putThread, start putting 第2个元素
putThread, end putting 第2个元素
putThread, start putting 第3个元素
putThread, end putting 第3个元素
putThread, start putting 第4个元素
putThread, end putting 第4个元素
putThread, start putting 第5个元素
putThread, end putting 第5个元素
putThread, start putting 第6个元素
start taking element
finish taking element: putThread第1元素
putThread, end putting 第6个元素
posted @ 2017-10-29 16:38  jennyjj  阅读(196)  评论(0编辑  收藏  举报