BlockingQueue使用方式 及 源码阅读

BlockingQueue使用方式 及 源码阅读

心血来潮,想了解一下BlockingQueue的实现原理。这里以ArrayBlockingQueue为例子分两部分进行了解:

  • 使用方式
  • 源码阅读

ArrayBlockingQueue 的源码实现中用到了ReentrantLock(重入锁),对其使用方式不太了解的同学可以查看:
ReentrantLock(重入锁)使用方式

使用方式

BlockingQueue queue = new ArrayBlockingQueue(3);
// 向队列中插入数据
queue.put(1);
// 从队列中取出数据
queue.take();

源码阅读

注:源码来自android-25

先看ArrayBlockingQueue构造方法:

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();
}

ArrayBlockingQueue中插入数据:

public void put(E e) throws InterruptedException {
    // 判空
    Objects.requireNonNull(e);
    // 重入锁对象
    final ReentrantLock lock = this.lock;
    // 获取可中断锁
    lock.lockInterruptibly();
    //
    try {
        // 如果队列已满,阻塞当前线程,等待队列释放出空间后的 notFull.signal() 信号
        while (count == items.length){
            notFull.await();
        }
        // 如果队列未满,则将数据插入队列中
        enqueue(e);
    } finally {
        // 释放锁
        lock.unlock();
    }
}

// 向队列中插入数据
private void enqueue(E x) {
    // 数组构造的队列
    final Object[] items = this.items;
    // 数据添加到队列末尾
    items[putIndex] = x;
    // putIndex +1 与数组长度相同
    if (++putIndex == items.length) {
        // index 归零,下次从index为0插入数据
        putIndex = 0;
    }
    // 队列中item数量
    count++;
    // 发送数组不为空的信号,唤醒对应的线程
    notEmpty.signal();
}
  • 向队列中插入数据时,如果队列已经满了,则调用notFull.await()让线程进入等待状态;直到队列中取出数据notFull.signal()信号发出后,才能重新唤醒添加数据的线程。
  • 向队列中插入数据时,不断向后边插入数据。如果当前插入的数据,是数组的最后一条数据,(只要数组不满)则下次从index为0的数组位置继续依次插入数据,从而形成一个循环。

ArrayBlockingQueue中出队列:

public E take() throws InterruptedException {
    // 重入锁
    final ReentrantLock lock = this.lock;
    // 获取可中断锁
    lock.lockInterruptibly();
    //
    try {
        // 如果数组中没有数据,阻塞当前线程,进入等待状态,等待notEmpty.signal()信号
        while (count == 0){
            notEmpty.await();
        }
        // 如果数组中有数据 出队列
        return dequeue();
    } finally {
        // 释放锁
        lock.unlock();
    }
}

// 从队列中取出数据
private E dequeue() {
    // 数组
    final Object[] items = this.items;
    // 顺序取出数据
    E x = (E) items[takeIndex];
    // 数组置空
    items[takeIndex] = null;
    // 如果取出的数据是 数组的最后一条 下次从头开始取数据
    if (++takeIndex == items.length) {
        takeIndex = 0;
    }
    // 队列中数据的数量减少一个
    count--;
    // 忽略...
    if (itrs != null){
        itrs.elementDequeued();
    }
    // 发送 数据不为空 的信号
    notFull.signal();
    return x;
}
  • 出队列时,如果队列已经没有数据了,则调用notEmpty.await()让线程进入等待状态;直到线程中加入数据notEmpty.signal()信号发出后,才能唤醒取数据的线程。
  • 出队列时,不断从前向后取。如果当前取出的数据,是数组的最后一条数据,(只要数组不空)则下次从index为0的数组位置继续依次取数据,从而形成一个循环。

猜你喜欢

转载自blog.csdn.net/aiwusheng/article/details/80774479
今日推荐