区别
- ,ArrayBlockingQueue 与 ArrayList 的内部存储结构为数组;LinkedBlockingQueue 存储是一个单向链表; LinkedBlockingDeque 与 LinkedList 是一个存储双向链表 (后三者都有指引head与tail的Node)
- ArrayBlockingQueue的初始化时是一定要外部指定容量大小的;而LinkedBlockingQueue & LinkedBlockingDeque提供默认容量Integer.MAX_VALUE。
- 对于消费和生产: ArrayBlockingQueue & LinkedBlockingDeque 通过同一把锁的不同Condition来控制;而LinkedBlockingQueue则通过锁分离的方式来控制。(其生产与消费所竞争的资源是属于2把锁,
生产线程要唤醒生产线程,消费线程也要唤醒生产线程; 消费线程唤醒消费线程,消费线程也要唤醒生产线程)
综述
两者都是先加锁ReentrantLock, 依赖于这个锁对于 [出队]等待队列 (Condition -> notEmpty)与 【入队】等待队列(Condition -> notFull) 来控制高并发下的出入。(锁默认用的是非公平模式:NonfairSync)
可以这么理解:
- notEmpty: 当前队列不是空的,可以消费; 所以出队时,判定队列为空,则await这个信号;队列不为空则进行出队操作,成功后唤醒 notFull ;
- notFull : 当前队列不是满的, 可以生产; 所以入队时,判定队列已满,则await这个信号。队列没满则进行入队操作,成功后唤醒notEmpty;
出队
拿ArrayBlockingQueue 的出队来分析:
- 先加锁。
- 判定当前的数量是否为空
- 如果为空,则notEmpty(“队列不是空的”信号) 等待。
- 如果不为空,则出队。 出队成功后, 唤醒 notFull ( "队列不是满的" 信号)。
- 最终释放锁。
入队
拿LinkedBlockingDeque 的入队来分析:
- 先加锁。
- 尝试出队操作:
- 在unlinkFirst方法中, 从首节点进行出队,出队完毕后,设置好新的First-Node, 唤醒 notFull ( "队列不是满的" 信号)。
- 如果出队返回是null,则 notEmpty(“队列不是空的”信号) 等待。
- 在unlinkFirst方法中, 从首节点进行出队,出队完毕后,设置好新的First-Node, 唤醒 notFull ( "队列不是满的" 信号)。
例子
模拟ArrayBlockingQueue的生产和消费方式
package com.noob.learn.netty;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Storage {
private int capacity = 10;
private Queue<Object> queue = new LinkedList<Object>();
final ReentrantLock lock = new ReentrantLock();
/** 当前队列不是空的,可以消费 */
private final Condition notEmpty = lock.newCondition();
/** 当前队列不是满的, 可以生产 */
private final Condition notFull = lock.newCondition();
public void produce() {
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
while (queue.size() > capacity - 1) {
System.out.println("库存量:" + queue.size() + "已满, 暂时不能执行生产任务!");
notFull.await();
}
queue.add(new Object());
System.out.println("生产了, 现仓储量为:" + queue.size());
notEmpty.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void consume() {
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
while (queue.size() == 0) {
System.out.println("库存量" + queue.size() + "暂时不能执行消费任务!");
notEmpty.wait();
}
queue.remove();
System.out.println("消费了, 现仓储量为:" + queue.size());
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Storage storage = new Storage();
new Thread(() -> {
while (true) {
storage.produce();
try {
Thread.sleep(1000); // 执行太快了降速
} catch (Exception e) {
e.printStackTrace();
}
}
} ).start();
new Thread(() -> {
while (true) {
storage.consume();
try {
Thread.sleep(1000);// 执行太快了降速
} catch (Exception e) {
e.printStackTrace();
}
}
} ).start();
}
}
执行结果: