眼睛看到的不是你的,但你看到之后将其思想复刻后的产物便是你的。(什么是借鉴?)
本文抱着互相学习分享、技术积累使其慢慢沉淀的态度写作,如有错误望各位大佬同仁指正。
文中出现代码来自 jdk 1.8
队列
FIFO(先进先出)的数据结构即为队列
阻塞队列
操作会被阻塞的队列即为阻塞队列, 在java中 BlockingQueue 接口在 Queue 接口的基础上增加了两组阻塞方法, offer(e,time) put , poll(time) take()
阻塞队列的几个操作方法
抛出异常 | 特殊值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除 | remove() | poll() | take() | poll(time,unit) |
检查 | element() | peek() | 不可用 | 不可用 |
一个使用数组实现的有界阻塞队列. 创建队列时必须给定队列大小, 同时可以通过创建队列的时候设置公平访问(通过重入锁的公平访问实现)
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();
}
根据调用不同的方法, 返回不同的状态
add 方法将抛出异常
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
offer 方法将返回 false
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
put 方法将无限时阻塞
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();
}
}
offer(E e, long timeout, TimeUnit unit) 将超时阻塞
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
poll 方法返回空
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
take 将无限时阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
poll(long timeout, TimeUnit unit) 超时阻塞
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}
总结 :
该队列模式适合在需要公平访问的场景下使用, 若无公平性要求该队列个人拙见不建议使用, 因操作数组和公平性原因,其吞吐量较低
一个使用链表实现的有界队列
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
如果不指定大小, 默认值为 int 的最大值
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
与 [ArrayBlockingQueue](#1. ArrayBlockingQueue[有界]) 相同
总结:
通过链表实现的一个双端阻塞队列(LikedBlockingQueue增加了队尾的操作)
该队列增加了一组队首队尾的操作方法
add -> addFirst(push)/addLast
offer -> offerFirst/offerLast
offer(time) ->offerFirst(time)/offerLast(time)
peek -> peekFirst/peekLast
poll -> pollFirst/pollLast
poll(time) -> pollFirst(time)/pollLast(time)
put -> putFrist/putLast
增加类(add/put/offer) 原方法调用与其调用相同前缀last方法操作相同
例: add = addLast ; put = putLast
获取类(peek/poll/element) 原方法调用与其调用相同前缀first方法操作相同
例: peek = peekFirst; poll = peekFirst
add 方法在队列容量达到最大值时抛出异常 throw new IllegalStateException("Deque full");
element/getFirst/getLast 方法在队列为空时抛出异常 if (x == null) throw new NoSuchElementException();
总结:
一个由链表实现的无界转换队列, 相对 [LinkedBlockingQueue](#2. LinkedBlockingQueue[有界]) 增加了几个方法
等待消费者调用返回
向队列的调用阻塞者直接提供元素, 如果没有人来获取, 则将这个元素放入队尾, 当这个元素出队的时候返回, 否则一直阻塞
调用一次即返回
尝试向队列的调用阻塞者直接提供元素, 立即返回false or true, 提供的元素不入队.
等待消费者调用返回, 一定时间内等不到亦返回
在 tryTransfer 的基础上加入了时间, 在给定时间内尝试
如果有阻塞调用者直接调用该队列的take 或者 poll(time) 方法, 阻塞状态下返回该值
如果未有阻塞调用者调用, 将元素放入队尾, 当在给定时间内被调用 返回 true, 如果在给定时间内未被调用, 返回false 且元素从队列中移除.
总结:
一个使用数组 + 比较器实现的优先级队列
这个队列使用了二叉堆排序的方式来实现优先级
关于这个队列的重点内容也是在二叉堆排序上, 这里延伸的内容还是比较多的, 堆结构, 二叉堆, 堆排序, 选择排序…
总结:
使用 PriorityQueue 实现的一个无界延迟队列, 使用这个队列需要自己实现一些内容, 包括延迟配置、比较器的实现。该队列可以用于定时任务调度,周期任务调度
当你需要指定元素的优先级,执行的时机,那这个队列即是不二之选。
总结:
dual queue + dual stack 双队列 + 双栈实现
这个队列也是一个比较特殊的队列, 在 JDK 1.6的时候改写了底层实现, 就是用了上面提到的方法.
这个队列是一个没有容量的队列,所以在调用方法上有一些不同。
add 方法将会抛出一个 java.lang.IllegalStateException: Queue full
异常
offer 方法会返回false
put 方法将会被阻塞
调用出队方法也会有一些问题
poll 方法返回null
take 方法将被阻塞
同步执行入队和出队即可, 这也是为什么该队列是吞吐量最高的队列原因
总结: