线程池的阻塞队列有多种实现可选,这里仅介绍ArrayBlockingQueue和LinkedBlockingQueue(鼓励大家多看源码的一些注释,讲的很清楚)
目录
ArrayBlockingQueue
- 有界,在创建的时候指定了大小,创建后,大小不可再更改
- 底层通过object数组存储
- 采用FIFO机制
- 队头是等待最久的任务,新任务加入阻塞队列的时候采用尾插法
- 内部结合一个ReetrantLock(取和放共用一个锁,所以取的时候没法放,放的时候没法取,吞吐量降低(相对LinkedBlockingQueue而言))和两个Condition使用,notEmpty条件用于从队列中取出一个任务,notFull条件表明可以将任务放入阻塞队列
- 构建ArrayBlockingQueue的时候,可以指定ReetrantLock是公平锁还是非公平锁
LinkedBlockingQueue
- 底层通过单链表实现
- 无界,因为是链表结构
- 默认最长长度是Integer.MAX_VALUE,可以通过构造函数来指定最长长度
- 采用FIFO机制
- 队头是等待最久的任务,新任务加入阻塞队列的时候采用尾插法
- 链接队列通常比基于数组的队列具有更高的吞吐量(吞吐量是指系统在单位时间内处理请求的数量,因为取操作和放操作采用了两个不同的锁,所以两个操作可以并行),但是在大多数并发应用程序中,可预测性能较差。
- 内部有两个ReetrantLock(takeLock,putLock)和两个Condition(notEmpty,notFull)
ArrayBlockingQueue部分源码
// 采用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();
}
}
// 尾插法插入,如果满了,就不插入,返回false,不满则插入,返回true
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();
}
}
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
LinkedBlockingQueue部分源码
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}