ArrayBlockingQueue 源码解读

130 阅读2分钟

16015500-8bf4856f92829bdc.png

ArrayBlockingQueue 创建存储结构 就是数组 大小为capacity false 默认非公平锁

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

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

offer 入队 写入操作 当队列满时 线程不会阻塞 直接false 添加失败

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 入队逻辑

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

add 方法其实和offer 一样

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

put 方法 会阻塞线程

public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        // 如果队列满了
        while (count == items.length)
            // 当前线程进入阻塞队列 condition
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

take 队列中没有元素然后去取 线程会阻塞

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            // 阻塞线程
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

dequeue 出队

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();
    //通知队列有位置了Put方法可以执行了  
    notFull.signal();
    return x;
}

poll 返回队列中的元素 如果没有返回Null 会删除对首元素

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

peek 可以执行多次 不会删除对列 对首元素

public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return itemAt(takeIndex); // null when queue is empty
    } finally {
        lock.unlock();
    }
}

总结

该队列使用的是数组来存放数据,如果当数据放满后,使用put方法则,阻塞住该线程,把该线程移入到未满条件队列中,等待取数据后被唤醒;如果取数据时,队列为空,则把该线程放入非空条件队列,等待放数据后被唤醒;因为使用的是数组,用写指针和读指针来标记位置,如果到了队尾,则会重新跳到队头;相当于是一个环;