区别
- ArrayBlockingQueue 、ArrayList 的内部存储结构为数组;LinkedBlockingQueue 是单向链表, LinkedBlockingDeque 、 LinkedList 是双向链表 (后三者都维护链表的head与tail引用node)
- ArrayBlockingQueue初始化时是一定要指定容量大小;ArrayList的默认大小为0;而LinkedBlockingQueue、LinkedBlockingDeque提供默认容量Integer.MAX_VALUE。
- ArrayBlockingQueue & LinkedBlockingDeque 通过同一把锁的不同Condition来控制;在操作时,生产与消费需要竞争同一把锁。
LinkedBlockingQueue通过锁分离方式来控制(生产与消费竞争的资源是属于独立的锁。队列空时:有生产后需要唤醒等待的消费者;队列满时:有消费后需要唤醒等待的生产者;当然对于同属生产(消费)者的不同线程相互间也需要竞争生产(消费)锁)。
实现方案
两者都是先加锁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();
}
}
执行结果:
来源:oschina
链接:https://my.oschina.net/u/3434392/blog/3013662