JAVA中的阻塞队列与非阻塞队列

阻塞队列

BlockingQueue接口下的各个实现类及部分源码介绍,BlockingQueue 对插入操作、移除操作、获取元素操作提供了四种不同的方法用于不同的场景中使用:

  • 抛出异常,如果队列为空或队列已满直接抛异常
  • 返回特殊值(null 或 true/false)
  • 阻塞等待此操作,直到这个操作成功
  • 阻塞等待此操作,直到成功或者超时指定时间。
操作 抛异常 返回空 阻塞等待 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
不移除返回头 element() peek()
BlockingQueue,我们的关注点应该在 put(e) 和 take() 这两个方法,因为这两个方法是带阻塞的

ArrayBlockingQueue

ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类,底层采用数组来实现。

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

源码很简单吧,多余的不说了

LinkedBlockingQueue

不同于ArrayBlockingQueue,这里用到了两个锁,两个 Condition

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

由优先级堆支持的、基于时间的调度队列

PriorityBlockingQueue

由优先级堆支持的无界优先级队列

ConcurrentLinkedQueue

底层利用cas + 循环,cas失败进行加一次循环,无界队列。
最终将数据插入到队尾

取值同样也是cas操作,这里就不细说了,大年三十,准备睡觉吃饭吧

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

猜你喜欢

转载自blog.csdn.net/qq_37904966/article/details/113772686