我正在尝试使用ThreadPoolExecutor来安排任务,但遇到了一些问题。这是它陈述的行为:
我想要的行为是:
基本上我不希望任何任务被拒绝;我希望他们在无限队列中排队。但我确实想拥有maximumPoolSize线程。如果我使用无界队列,它在击中coreSize后就不会生成线程。如果我使用有界队列,它会拒绝任务。有没有办法解决这个问题?
我现在正在考虑的是在SynchronousQueue上运行ThreadPoolExecutor,但不直接向它提供任务 - 而是将它们提供给单独的无界LinkedBlockingQueue。然后另一个线程从LinkedBlockingQueue进入Executor,如果一个被拒绝,它只是再次尝试,直到它被拒绝。这看起来像是一种痛苦而且有点像黑客 - 有更清洁的方法吗?
答案 0 :(得分: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
。此时,自定义RejectedExecutionHandler
可以强制put
加入自定义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
}
答案 1 :(得分:1)
只需设置corePoolsize = maximumPoolSize
并使用无限制队列?
在您的积分列表中,1排除2,因为corePoolSize
总是小于或等于maximumPoolSize
。
修改强>
您想要的东西与TPE为您提供的东西之间仍然存在不相容之处。
如果您有一个无界的队列,maximumPoolSize
将被忽略,因此,正如您所观察到的,将不会创建和使用corePoolSize
个线程。
所以,再次,如果你带着corePoolsize = maximumPoolSize
无限队列,你有你想要的,不是吗?
答案 2 :(得分:1)
您是否正在寻找更像缓存线程池的内容?
答案 3 :(得分:1)
您的用例很常见,完全合法,不幸的是比人们预期的更难。有关背景信息,您可以阅读this discussion并找到指向解决方案的指针(也在线程中提到)here。 Shay的解决方案很好。
一般来说,我对无限排队有点警惕;通常情况下,明确的传入流控制会优雅地降级并调节当前/剩余工作的比例,以免压倒生产者或消费者。