阻塞队列之ArrayBlockingQueue

ArrayBlockingQueue

一:概述

ArrayBlockingQueue类是由数组支持的有界阻塞队列。通过有界,它意味着队列的大小是固定的。创建后,无法更改容量。尝试将元素放入完整队列将导致操作阻塞。同样,也会阻止从空队列中获取元素的尝试。最初可以通过将容量作为ArrayBlockingQueue的构造函数中的参数传递来实现ArrayBlockingQueue的绑定。此队列命令元素FIFO(先进先出)。新插入的元素始终插入队列的尾部,队列检索操作获取队列头部的元素

二:ArrayBlockingQueue详解

1.构造函数

构造函数
// 使用给定(固定)容量和默认访问策略创建ArrayBlockingQueue。
public ArrayBlockingQueue(int capacity) {
}

// 使用给定(固定)容量和指定的访问策略创建ArrayBlockingQueue。如果fair为真,那么在插入或删除时阻塞的线程的队列访问将按FIFO顺序处理; 如果为false,则未指定访问顺序。
public ArrayBlockingQueue(int capacity, boolean fair) {
    // 初始容量必须大于0
        if (capacity <= 0)
            throw new IllegalArgumentException();
        // 初始化数组
        this.items = new Object[capacity];
        // 初始化可重入锁
        lock = new ReentrantLock(fair);
        // 初始化等待条件
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
}

//创建一个具有给定(固定)容量的ArrayBlockingQueue,指定的访问策略,最初包含给定集合的元素,以集合迭代器的遍历顺序添加。如果fair为真,那么在插入或删除时阻塞的线程的队列访问将按FIFO顺序处理; 如果为false,则未指定访问顺序。
public ArrayBlockingQueue(int capacity, boolean fair,
                          Collection<? extends E> c) {
    // 调用两个参数的构造函数
        this(capacity, fair);
        // 可重入锁
        final ReentrantLock lock = this.lock;
        // 上锁
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) { // 遍历集合
                    // 检查元素是否为空
                    checkNotNull(e);
                    // 存入ArrayBlockingQueue中
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) { // 当初始化容量小于传入集合的大小时,会抛出异常
                throw new IllegalArgumentException();
            }
            // 元素数量
            count = i;
            // 初始化存元素的索引
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            // 释放锁
            lock.unlock();
        }
}

2.类基本属性

类基本属性
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    // 存放实际元素的数组
    final Object[] items;
    // 取元素索引
    int takeIndex;
    // 获取元素索引
    int putIndex;
    // 队列中的项
    int count;
    // 可重入锁
    final ReentrantLock lock;
    // 等待获取条件
    private final Condition notEmpty;
    // 等待存放条件
    private final Condition notFull;
    // 迭代器
    transient Itrs itrs = null;
}

3.put函数解析

put函数解析
   public void put(E e) throws InterruptedException {
        checkNotNull(e);
        // 获取重入锁 
        final ReentrantLock lock = this.lock;
       /* 1.如果当前线程未被中断,则获取锁。  
          2.如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。
          3.如果当前线程已经保持此锁,则将保持计数加 1,并且该方法立即返回。 
          4.如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以
前,该线程将一直处于休眠状态: 
     		1)锁由当前线程获得;或者 
    	    2)其他某个线程中断当前线程。 
		  5.如果当前线程获得该锁,则将锁保持计数设置为 1。 
   如果当前线程: 
       		1)在进入此方法时已经设置了该线程的中断状态;或者 
       		2)在等待获取锁的同时被中断。 
   则抛出 InterruptedException,并且清除当前线程的已中断状态。
   */
       lock.lockInterruptibly();
        try {
            // // 判断元素是否已满
            while (count == items.length)
                // // 若满,则等待
                notFull.await();
            // // 入队列
            enqueue(e);
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

put函数用于存放元素,当线程被中断时会抛出异常,如果队列已满则会一直阻塞等待,当队列有空余位置,则enqueue:

enqueue源码
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)
            // 重置索引为0
            putIndex = 0;
        // 元素数量加1
        count++;
        // 唤醒在notEmpty(等待获取)条件上等待的线程
        notEmpty.signal();
    }

4.offer函数解析

offer函数解析
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();
    }
}

offer函数也用于存放元素,在调用ArrayBlockingQueue的add方法时,会间接的调用到offer函数(super.add()方法里面调用的就是offer方法),offer函数添加元素不会抛出异常,当底层Object数组已满时,则返回false,否则,会调用enqueue函数(这部分逻辑同put函数),将元素存入底层Object数组。并唤醒等待notEmpty条件的线程。


5.take函数解析

take函数解析
public E take() throws InterruptedException {
    // 可重入锁
    final ReentrantLock lock = this.lock;
    // 当前线程未被中断,则获取锁,中断会抛出异常 同put函数
    lock.lockInterruptibly();
    try {
        // 元素数量为0,即Object数组为空
        while (count == 0)
            // 等待,直到有元素
            notEmpty.await();
        // 出队列
        return dequeue();
    } finally {
        // 释放锁
        lock.unlock();
    }
}

take函数用于从ArrayBlockingQueue中获取一个元素,其与put函数相对应,在当前线程被中断时会抛出异常,并且当队列为空时,会阻塞一直等待,直到队列有元素。出队过程主要由 Dequeue 函数完成:

Dequeue 函数解析
private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
    	// 根据takeIndex获取元素
        E x = (E) items[takeIndex];
    	// 取出元素之后,将此索引的值赋值为null
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            // 重新赋值取值索引
            takeIndex = 0;
        // 元素个数减1
        count--;
        if (itrs != null)
            itrs.elementDequeued();
    	// 唤醒在notFull条件上等待的线程
        notFull.signal();
        return x;
    }

6.poll函数

poll 函数解析
public E poll() {
    // 重入锁
    final ReentrantLock lock = this.lock;
    // 获取锁
    lock.lock();
    try {
        // 若元素个数为0则返回null,否则,调用dequeue,出队列
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

poll函数用于获取元素,其与offer函数相对应,不会抛出异常,当元素个数为0是,返回null,否则,调用dequeue函数,并唤醒等待notFull条件的线程。并返回。


7.clear函数

clear 函数解析
public void clear() {
    // 数组
    final Object[] items = this.items;
    // 重入锁
    final ReentrantLock lock = this.lock;
    // 获取锁
    lock.lock();
    try {
        // 元素的个数
        int k = count;
        if (k > 0) {// 元素个数大于0
            // 存元素索引
            final int putIndex = this.putIndex;
            // 取元素索引
            int i = takeIndex;
            do {
                // 取元素同时将索引赋值为null
                items[i] = null;
                if (++i == items.length)
                    i = 0;
            } while (i != putIndex);
            takeIndex = putIndex;
            count = 0;
            if (itrs != null)
                itrs.queueIsEmpty();
            for (; k > 0 && lock.hasWaiters(notFull); k--)
                notFull.signal();
        }
    } finally {
        lock.unlock();
    }
}

三:简单使用

① add方法
public class ArrayBlockingQueueDemo1 {
    public static void main(String[] args) {
        // define capacity of ArrayBlockingQueue
        int capacity = 3;

        // create object of ArrayBlockingQueue
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(capacity);

        // Add element to ArrayBlockingQueue
        queue.add(1);

        System.out.println("After adding 1");
        System.out.println(queue);

        queue.add(2);
        queue.add(3);
        queue.add(4);
        System.out.println("After adding four element");
        System.out.println(queue);
    }
}
当添加元素少于等于3个时候,结果为:
After adding 1 [1]
After adding four element
[1, 2, 3]
当添加元素大于3个,运行结果异常:Exception in thread "main" java.lang.IllegalStateException: Queue full (实际调用的是AbstractQueue里的add方法)
-----
② put方法
public class ArrayBlockingQueueDemo2 {
    public static void main(String[] args)  {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
        try {
            queue.put(1);
            queue.put(2);
            queue.put(3);
            queue.put(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("queue contains " + queue);
    }
}
当添加元素大于3时候,会阻塞当前线程,直到有空余的元素
-------
③ take方法
public class ArrayBlockingQueueDemo3 {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
        queue.add(1);
        queue.add(2);
        queue.add(3);
        System.out.println("After addding numbers Queue: " +queue);

        int head=queue.take();
        System.out.println("Head of queue removed is " +head);
        System.out.print("After removing head,Queue: ");
        System.out.println(queue);

        head = queue.take();

        System.out.println("Head of queue removed is " + head);
        System.out.print("After removing head Queue: ");
        System.out.println(queue);
    }
}
After addding numbers Queue: [1, 2, 3]
Head of queue removed is 1
After removing head,Queue: [2, 3]
Head of queue removed is 2
After removing head Queue: [3]
-------
④ drainTo方法
public class ArrayBlockingQueueDemo4 {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue(3);
        queue.add(1);
        queue.add(2);
        queue.add(3);
        System.out.println("Before drainTo Operation");
        System.out.println("queue = " + queue);

        ArrayList<Integer> list = new ArrayList();

        queue.drainTo(list);
        System.out.println("After drainTo Operation");
        System.out.println("queue = " + queue);
        System.out.println("collection = " + list);
    }
}
Before drainTo Operation
queue = [1, 2, 3]
After drainTo Operation
queue = []
collection = [1, 2, 3]
------
④ poll方法
public class ArrayBlockingQueueDemo5 {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        System.out.println("Queue Contains" + queue);
        System.out.println("Removing From head: " + queue.poll());
        System.out.println("Queue Contains" + queue);
        System.out.println("Removing From head: " + queue.poll());
        System.out.println("Queue Contains" + queue);
        System.out.println("Removing From head: " + queue.poll());
        System.out.println("Queue Contains" + queue);
        System.out.println("Removing From head: " + queue.poll());
        System.out.println("Queue Contains" + queue);
    }
}
Queue Contains[1, 2, 3]
Removing From head: 1
Queue Contains[2, 3]
Removing From head: 2
Queue Contains[3]
Removing From head: 3
Queue Contains[]
Removing From head: null
Queue Contains[]
----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mindcarver

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值