Blocking queue BlockingQueue (ArrayBlockingQueue) source code analysis

阻塞队列有多种实现,这里我们就先分析一下ArrayBlockingQueue,该队列获取数据、插入数据、检测数据的方式有多种,但是不同的方法会有不同的效果,比如插入数据时,如果队列已经满了,调用add方法插入会抛异常,但是是有put插入数据就会阻塞当前线程。具体的效果查看下面的图。
Basic method of queue
Here we just focus on the blocking methods, put and take methods, to see how they block.

1. Create a bounded blocking queue of length 2

BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(2);

2. Analyze the constructor of the queue

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

capacity: queue length
fair: whether it is a fair lock, the default is a non-fair lock
items: an array used to save data, there are other ways to use other queues, such as linked list
lock: lock, the lock used to operate the current queue
notEmpty: use To store threads blocked when there is no data in the
queue notFull: used to store threads blocked when there is no space in the queue

lock.newCondition()是创建一个队列,对于不可进行争夺锁的线程阻塞放入到该队列中,后续可将该队列所有线程全部唤醒,放入到AQS队列,详细说明可查看ReentrantLock源码分析。

Two waiting queues are created here, which are used to store blocking queues with no space and blocking queues with no data.

3.put method to insert data

public void put(E e) throws InterruptedException {
    
    
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();  // 可中断的获取锁
    try {
    
    
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
    
    
        lock.unlock();
    }
}

Determine whether the current queue data is full, if it is full, block the current thread, if not, set the value

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)
        putIndex = 0;
    count++;
    notEmpty.signal();
}

There is a detail here that if the pointer to add data reaches the end of the array, it will restart from the beginning of the array, and the position of data insertion is marked by the putIndex pointer.
After inserting data, wake up the thread group waiting to obtain the queue data, notEmpty. signal();

4. The take method gets the value

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

Determine whether there is a value in the current queue, if there is no value, the current thread is blocked and added to the waiting queue. get if there is a value

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

There is also a detail here, when the acquired value is the last digit of the array, set the pointer takeIndex of the acquired value to the beginning of the array. Finally, it will wake up the thread group waiting to add data: notFull.signal();

5. Summary

1. The position of inserting data in the queue and the position of obtaining data are marked by two pointer subscripts. When the pointer reaches the last bit of the array, the pointer will be reset to 0, that is, it will be used repeatedly from the beginning.

2. Whenever data is inserted, it will trigger the wake-up of the thread group waiting to obtain data. On the contrary, every time data is obtained, it will trigger the wake-up of the thread group waiting to insert data.

Guess you like

Origin blog.csdn.net/weixin_33613947/article/details/116743066