并发编程-ArrayBlockingQueue源码分析

1、ArrayBlockingQueue成员变量维护了take下标,put下标,元素数量,一个ReentrantLock锁,notEmpty条件,notFull条件

public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
    //数组,存储元素     
    final Object[] items;
    //取元素下标
    int takeIndex;
    //放元素下标
    int putIndex;
    //元素数量
    int count;
    //使用一个锁,实现 1、put数据到队列时,take数据的线程进入等待队列,阻塞并挂起,2、take数据时,put数据的线程进入等待队列,阻塞并挂起
    final ReentrantLock lock;
    //条件队列
    private final Condition notEmpty;
    //条件队列
    private final Condition notFull;
    //
    transient ArrayBlockingQueue<E>.Itrs itrs;
    
}

2、阻塞队列put数据操作逻辑

a、put请求到到达后,先调用lock.lockInterruptibly()方法加锁

b、判断当前队列是否已满

  • 满了,则调用notFull.await释放锁,当前线程进入阻塞状态,等待唤醒获取锁
  • 不满,执行入队逻辑(元素放入到数组下标为puIndex的内存位置,putIndex下标加1,并判断putIndex下标是否等于队列长度,等于则设置putIndex为0,最后调用notEmpty.sigal()方法唤醒阻塞的take线程去抢占锁,注意此时的锁还未释放)

c、调用lock.unlock()释放锁

public void put(E var1) throws InterruptedException {
    //检查数据是否为空,为空抛出异常
    checkNotNull(var1);
    ReentrantLock var2 = this.lock;
    //加锁
    var2.lockInterruptibly();
    try {
        //##循环的作用:当阻塞的线程被唤醒后,再次进行循环条件判断队列是否已满,不满就跳出循环执行后续逻辑##  
        while(this.count == this.items.length) {
        //1、队列满了,调用Condition.await()方法释放锁,线程挂起(进入WAIT无时限等待状态),等待唤醒(唤醒后进入阻塞状态获取锁,再进入就绪状态)
        this.notFull.await();
        }
        //2、队列不满,执行入队
        this.enqueue(var1);
    } finally {
        //2.1、入队完成之后,释放锁    
        var2.unlock();
    }

}

private void enqueue(E var1) {
    Object[] var2 = this.items;
    //1、把元素放入到数组的putIndex下标位置
    var2[this.putIndex] = var1;
    //2、putIndex下标加1,并判断putIndex是否等于数组长度,等于则设置putIndex为0
    if (++this.putIndex == var2.length) {
        this.putIndex = 0;
    }
    //3、数组元素加1
    ++this.count;
    //4、通过condition.sigal()唤醒等待的take线程去抢占锁,注意此时只是唤醒,但是锁还未被释放
    this.notEmpty.signal();
}

3、阻塞队列take数据逻辑

a、take数据请求到达后,调用lock.lockInterruptibly()方法加锁

b、循环队列是否为空

  • 为空,则调用Condition.await()方法释放锁,线程进入阻塞状态,等待获取锁
  • 不为空,则执行出队逻辑(取出数组下标为takeIndex的元素,takeIndex加1,判断takeIndex是否等于数组长度,等于则设置takeIndex为0,最后调用Condition.sigal()方法唤醒put数据的阻塞线程准备抢占锁,注意此时锁还未释放)

c、调用lock.unlock()方法释放锁

public E take() throws InterruptedException {
        ReentrantLock var1 = this.lock;
       //加锁
        var1.lockInterruptibly();
        Object var2;
        try {
            //##循环的作用:当阻塞的线程被唤醒后,会再次进入循环判断队列是否为空,不为空跳出循环执行后续逻辑##
            while(this.count == 0) {
                 //1、为空则调用await()方法,take线程释放锁并挂起(进入WAIT无时限等待状态),等待唤醒(唤醒后进入阻塞状态获取锁,再进入就绪状态)
                this.notEmpty.await();
            }
            //调用出队逻辑    
            var2 = this.dequeue();
        } finally {
            //释放锁    
            var1.unlock();
        }
        return var2;
}

private E dequeue() {   
        Object[] var1 = this.items;
        //根据takeIndex下标获取队列中的数据
        Object var2 = var1[this.takeIndex];
        //取出后takeIndex下标位置数据置空
        var1[this.takeIndex] = null;
        //takeIndex加1,并判断takeIndex跟队列长度是否相等
        if (++this.takeIndex == var1.length) {
        //相等则takeIndex置为0    
            this.takeIndex = 0;
        }
         //队列元素数量减1
        --this.count;
        //
        if (this.itrs != null) {
            //    
            this.itrs.elementDequeued();
        }
        //调用notFull.sigal()方法,通知唤醒put阻塞线程,准备获取锁,注意此时take线程还没释放锁
        this.notFull.signal();
        return var2;
}

 

posted @ 2023-07-02 10:01  西风51668  阅读(25)  评论(0)    收藏  举报