无法创建具有大小限制的缓存线程池?

似乎不可能创建缓存线程池,但对它可以创建的线程数有限制。

以下是在标准 Java 库中实现静态的方法:Executors.newCachedThreadPool

 public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

因此,使用该模板继续创建固定大小的缓存线程池:

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());

现在,如果您使用它并提交3个任务,一切都会好起来的。提交任何其他任务将导致拒绝执行异常。

试试这个:

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());

将导致所有线程按顺序执行。也就是说,线程池永远不会产生多个线程来处理您的任务。

这是 的执行方法中的一个错误?或者也许这是故意的?还是有其他方法?ThreadPoolExecutor

编辑:我想要的东西与缓存的线程池完全相同(它按需创建线程,然后在一段时间超时后杀死它们),但对它可以创建的线程数量有限制,并且能够在达到线程限制后继续排队其他任务。根据sjlee的回应,这是不可能的。看它的方法确实是不可能的。我需要像do一样进行子类化和覆盖,但是它所做的是一个完整的黑客攻击。execute()ThreadPoolExecutorThreadPoolExecutorexecute()SwingWorkerSwingWorkerexecute()


答案 1

具有以下几个关键行为,您的问题可以通过这些行为来解释。ThreadPoolExecutor

提交任务后,

  1. 如果线程池尚未达到内核大小,则会创建新线程。
  2. 如果已达到内核大小并且没有空闲线程,则会对任务进行排队。
  3. 如果已达到内核大小,则没有空闲线程,并且队列变满,则会创建新线程(直到达到最大大小)。
  4. 如果已达到最大大小,则没有空闲线程,并且队列已满,则拒绝策略将启动。

在第一个示例中,请注意,实质上的大小为 0。因此,当您达到最大大小 (3) 时,拒绝策略就会生效 (#4)。SynchronousQueue

在第二个示例中,选择的队列是具有无限大小的队列。因此,您会遇到行为#2。LinkedBlockingQueue

您不能真正修改缓存类型或固定类型,因为它们的行为几乎完全确定。

如果要具有有界和动态线程池,则需要使用正内核大小和最大大小以及有限大小的队列。例如

new ThreadPoolExecutor(10, // core size
    50, // max size
    10*60, // idle timeout
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<Runnable>(20)); // queue with a size

附录:这是一个相当古老的答案,当涉及到内核大小为0时,JDK似乎改变了它的行为。从 JDK 1.6 开始,如果内核大小为 0 并且池没有任何线程,则 ThreadPoolExecutor 将添加一个线程来执行该任务。因此,核心大小 0 是上述规则的例外。感谢史蒂夫引起我的注意。


答案 2

除非我错过了什么,否则原始问题的解决方案很简单。下面的代码实现了原始海报中描述的所需行为。它将生成多达 5 个线程来处理无限队列,空闲线程将在 60 秒后终止。

tp = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
tp.allowCoreThreadTimeOut(true);

推荐