13

我正在尝试使用 ThreadPoolExecutor 来安排任务,但在其策略上遇到了一些问题。这是其声明的行为:

  1. 如果运行的线程少于 corePoolSize,则 Executor 总是更喜欢添加新线程而不是排队。
  2. 如果 corePoolSize 或更多线程正在运行,Executor 总是更喜欢排队请求而不是添加新线程。
  3. 如果请求无法排队,则会创建一个新线程,除非这将超过 maximumPoolSize,在这种情况下,该任务将被拒绝。

我想要的行为是这样的:

  1. 和上面一样
  2. 如果超过 corePoolSize 但小于 maximumPoolSize 线程正在运行,则更喜欢添加新线程而不是排队,并且使用空闲线程而不是添加新线程。
  3. 和上面一样

基本上我不希望任何任务被拒绝;我希望他们在无界队列中排队。但我确实希望拥有最多 maximumPoolSize 线程。如果我使用无界队列,它在达到 coreSize 后永远不会生成线程。如果我使用有界队列,它会拒绝任务。有没有办法解决?

我现在正在考虑的是在 SynchronousQueue 上运行 ThreadPoolExecutor,但不直接将任务提供给它 - 而是将它们提供给单独的无界 LinkedBlockingQueue。然后另一个线程从 LinkedBlockingQueue 提供给 Executor,如果一个被拒绝,它会再次尝试,直到它没有被拒绝。不过,这似乎是一种痛苦和一种黑客行为 - 有没有更清洁的方法来做到这一点?

4

4 回答 4

4

可能没有必要根据请求对线程池进行微管理。

缓存线程池将重用空闲线程,同时还允许潜在的无限并发线程。这当然可能导致突发期间上下文切换开销的失控性能下降。

Executors.newCachedThreadPool();

更好的选择是限制线程总数,同时放弃确保首先使用空闲线程的概念。配置更改将是:

corePoolSize = maximumPoolSize = N;
allowCoreThreadTimeOut(true);
setKeepAliveTime(aReasonableTimeDuration, TimeUnit.SECONDS);

对这种情况进行推理,如果执行程序的线程数少于corePoolSize线程数,那么它一定不会很忙。如果系统不是很忙,那么启动一个新线程几乎没有什么坏处。ThreadPoolExecutor如果它低于允许的最大工人数,这样做将导致您始终创建一个新工人。只有当最大数量的工人“运行”时,才会给空闲等待任务的工人分配任务。如果工作人员在aReasonableTimeDuration没有任务的情况下等待,则允许它终止。对池大小使用合理的限制(毕竟,只有这么多 CPU)和合理的大超时(以防止线程不必要地终止),可能会看到预期的好处。

最后的选择是hackish。基本上,ThreadPoolExecutor内部用于BlockingQueue.offer确定队列是否有容量。的自定义实现BlockingQueue可能总是拒绝offer尝试。当队列ThreadPoolExecutor中的任务失败时offer,它将尝试创建一个新的工作者。如果无法创建新的工作人员,RejectedExecutionHandler则会调用 a。那时, customRejectedExecutionHandler可以强制 aput进入 custom BlockingQueue

/** Hackish BlockingQueue Implementation tightly coupled to ThreadPoolexecutor implementation details. */
class ThreadPoolHackyBlockingQueue<T> implements BlockingQueue<T>, RejectedExecutionHandler {
    BlockingQueue<T> delegate;

    public boolean offer(T item) {
        return false;
    }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        delegate.put(r);
    }

    //.... delegate methods
}
于 2010-08-06T06:01:49.003 回答
1

只是设置corePoolsize = maximumPoolSize和使用无界队列?

在您的点列表中,1 不包括 2,因为corePoolSize将始终小于或等于maximumPoolSize

编辑

在您想要的和 TPE 将提供给您的之间仍然存在一些不相容的东西。

如果您有一个无界队列,则将maximumPoolSize被忽略,因此,正如您所观察到的,不会corePoolSize创建和使用线程。

所以,再一次,如果你corePoolsize = maximumPoolSize有一个无限的队列,你有你想要的,不是吗?

于 2010-08-05T21:41:04.970 回答
1

您会寻找更像缓存线程池的东西吗?

http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool()

于 2010-08-06T00:08:50.063 回答
1

您的用例很常见,完全合法,不幸的是比预期的要困难。有关背景信息,您可以阅读此讨论并在此处找到指向解决方案的指针(也在线程中提到)。谢伊的解决方案效果很好。

一般来说,我会对无界队列有点警惕;通常最好有明确的传入流控制,它可以优雅地降级并调节当前/剩余工作的比率,以免压倒生产者或消费者。

于 2010-08-06T08:01:50.703 回答