数据结构中队列就是先进先出的结构,加上点算法可以实现优先队列,再加上线程安全就可以形成下图
这里还多了个阻塞队列,阻塞队列可以凭很生产与消费的速率,即take/put的快慢调节,它是线程安全的,这样就将需要程序员维护的线程安全问题转移到队列上了。
一般数据结构类中都需要定义这个容器的大小,LinkedBlockingQueue 中这个上限值是Integer.MAX_VALUE。
阻塞队列提供了增删方法,但是这些方法可以分为三类,一类是当条件不满足时会抛出异常,一类是不满足条件时会返回bool值,不会抛异常,最后一类就时会阻塞了。
对应方法如下:
前面给出了java 库中队列的几种形式,其中
ArrayBlockingQueue 就是不可扩容了。类似于golang的指定长度chan。
LinkedBlockingQueue 很明显用链表实现的。
SynchronousQueue 没有容量,类似golang中没有长度的chan。
PriorityBlockingQueue 通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。
DelayQueue 内部使用PriorityQueue 排序,可以看大牛的代码设计是多么nice。
这么多阻塞队列可以按需取用,但是其根本的阻塞原理无外乎就是对临界区加解锁
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();
}
}
而非阻塞的ConcurrentLinkedQueue 则采用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) {
// p is last node
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
有这么多阻塞队列,那怎么采用呢,Java线程池的选用为
FixedThreadPool 线程数一定,任务过多时用LinkedBlockingQueue来接。
ScheduledThreadPool 用DelayedWorkQueue 来实现schedule.。
总结起来就是:
按功能选
需要排序、延迟的选PriorityBlockingQueue
按容量选
固定容量的选 ArrayBlockingQueue
按是否扩容选
从C++开发看,不得不说生态丰富,库函数齐全真是爽。