问题是因为你的任务队列太小了
恕我直言,这并不能回答 OP 问题(尽管 Kiril 在那之后提供了更多细节),因为队列的大小 太小 完全是主观的。例如,他可能正在保护一个无法处理两个以上并发请求的外部资源(除此之外,我认为 2 是为了进行快速测试)。另外,我们可以说多大的尺寸不算小? 1000?如果执行尝试执行 5000 个任务怎么办?场景仍然存在,因为真正的问题是如果 ThreadPoolExecutor 使用 LinkedBlockingQueue,为什么生产者或调用者线程没有被阻塞?
BlockingQueue 的作用是完成生产者/消费者模式,如果它足够大,那么你应该看不到你遇到的问题。正如我上面提到的,您需要增加队列大小并捕获异常,然后重试执行任务。
如果您知道可以在运行时排队的最大任务数,并且可以为此分配足够的堆,则这是正确的,但情况并非总是如此。使用有界队列的好处是您无法保护自己免受OutOfMemoryError 的侵害。
正确答案应该是来自@gkamal 的答案。
阻塞队列主要用于消费者(池中的线程)。线程可以等待队列中的新任务可用,它们将被自动唤醒。一个普通的链表不能达到这个目的。
我的两分钱:
开发人员可能已经决定消费者不会阻塞,在这种情况下将不再需要 BlockingQueue(尽管您仍然需要使用并发集合或手动处理同步),但在这种情况下,当工作人员尝试 轮询你必须从一个空集合中的一个任务
- 杀死线程(处理新任务创建新线程),或
- 忙着等待浪费cpu
由于创建新线程是一项昂贵的任务(毕竟这是我们使用线程池的方式)并且会忙于等待,因此最好的选择是阻止该消费者并稍后在任务可用时通知。
实际上,有一种特殊情况,当线程数超过配置的corePoolSize 时,worker 不会阻塞(至少不是无限期地)。
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
我们可以在 ThreadPoolExecutor (java 1.8) 中看到这两种场景
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait, and if the queue is
* non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*/
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // <---- wait at most keepAliveTime
workQueue.take(); // <---- if there are no tasks, it awaits on notEmpty.await();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}