Blocking queue and non-blocking queue in JAVA

Blocking queue

Each implementation class under the BlockingQueue interface and part of the source code introduction, BlockingQueue provides four different methods for inserting, removing, and getting element operations for use in different scenarios:

  • Throw an exception, if the queue is empty or the queue is full, throw an exception directly
  • Return special value (null or true/false)
  • Block waiting for this operation until this operation succeeds
  • Block waiting for this operation until it succeeds or the specified time expires.
operating Throw exception Return empty Blocking wait time out
insert add(e) offer(e) put(e) offer(e, time, unit)
Remove remove() poll() take() poll(time, unit)
Do not remove the return header element() peek()
BlockingQueue,我们的关注点应该在 put(e) 和 take() 这两个方法,因为这两个方法是带阻塞的

ArrayBlockingQueue

ArrayBlockingQueue is a bounded queue implementation class of the BlockingQueue interface, and the bottom layer is implemented by an array.

put

blockingQueue.put(ball);

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();
   }
}
private void enqueue(E x) {
    
    
    //底层使用数组实现的,数组下标赋值
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    //唤醒非空等待,就是唤醒取值等待
    notEmpty.signal();
}

blockingQueue.take()

public E take() throws InterruptedException {
    
    
    //获取锁
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
    
    
       //非空阻塞
        while (count == 0)
            notEmpty.await();
        //取队列数据
        return dequeue();
    } finally {
    
    
        lock.unlock();
    }
}
private E dequeue() {
    
    
     //取数组下标数据 老下标 置空
    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;
}

The source code is very simple, let's not talk about the extra

LinkedBlockingQueue

Different from ArrayBlockingQueue, two locks and two Condition are used here

public void put(E e) throws InterruptedException {
    
    
     if (e == null) throw new NullPointerException();
     //用于唤醒读线程取值
     int c = -1;
     Node<E> node = new Node<E>(e);
     //获取put锁
     final ReentrantLock putLock = this.putLock;
     final AtomicInteger count = this.count;
     //上锁
     putLock.lockInterruptibly();
     try {
    
    
         //队列是否满了 满了阻塞notFull
         while (count.get() == capacity) {
    
    
             notFull.await();
         }
         //加入队列
         enqueue(node);
         c = count.getAndIncrement();
         //队列没有满 唤醒  另一个put线程
         if (c + 1 < capacity)
             notFull.signal();
     } finally {
    
    
         putLock.unlock();
     }
     //如果 c == 0,那么代表队列在这个元素入队前是空的
     if (c == 0)
         //那么所有的读线程都在等待 notEmpty 这个条件,等待唤醒,这里做一次唤醒操作
         signalNotEmpty();
 }

enqueue

private void enqueue(Node<E> node) {
    
    
    last = last.next = node;
}
private void signalNotEmpty() {
    
    
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
    
    
        notEmpty.signal();
    } finally {
    
    
        takeLock.unlock();
    }
}

take

public E take() throws InterruptedException {
    
    
     E x;
     int c = -1;
     final AtomicInteger count = this.count;
     //take锁
     final ReentrantLock takeLock = this.takeLock;
     takeLock.lockInterruptibly();
     try {
    
    
         //如果队列等于0 阻塞住
         while (count.get() == 0) {
    
    
             notEmpty.await();
         }
         //取队列数据
         x = dequeue();
         //取前队列中的长度
         c = count.getAndDecrement();
         if (c > 1)
             //如果队列中有数据唤醒其余线程取数据
             notEmpty.signal();
     } finally {
    
    
         takeLock.unlock();
     }
      // 如果 c == capacity,那么说明在这个 take 方法发生的时候,队列是满的
    // 既然出队了一个,那么意味着队列不满了,唤醒写线程去写
     if (c == capacity)
         signalNotFull();
     return x;
 }

DelayQueue

Time-based scheduling queue backed by priority heap

PriorityBlockingQueue

Unbounded priority queue supported by priority heap

ConcurrentLinkedQueue

The bottom layer uses cas + loop, cas fails to add a loop, unbounded queue.
Finally insert the data to the end of the queue

The value is also a cas operation, so I won’t go into details here.

public boolean offer(E e) {
    
    
   checkNotNull(e);
   final Node<E> newNode = new Node<E>(e);

   for (Node<E> t = tail, p = t;;) {
    
    
       Node<E> q = p.next;
       if (q == null) {
    
    
           if (p.casNext(null, newNode)) {
    
    
               if (p != t) // hop two nodes at a time
                   casTail(t, newNode);  // Failure is OK.
               return true;
           }
       }
       else if (p == q)
           p = (t != (t = tail)) ? t : head;
       else
           p = (p != t && t != (t = tail)) ? t : q;
   }
}

Guess you like

Origin blog.csdn.net/qq_37904966/article/details/113772686