ThreadPoolExecutor策略

时间:2010-08-05 21:35:54

标签: java multithreading concurrency executor blockingqueue

我正在尝试使用ThreadPoolExecutor来安排任务,但遇到了一些问题。这是它陈述的行为:

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

    1. 与上述相同
    2. 如果运行的是corePoolSize以上但不到maximumPoolSize线程,则更喜欢在排队时添加新线程,并在添加新线程时使用空闲线程。
    3. 与上述相同
    4. 基本上我不希望任何任务被拒绝;我希望他们在无限队列中排队。但我确实想拥有maximumPoolSize线程。如果我使用无界队列,它在击中coreSize后就不会生成线程。如果我使用有界队列,它会拒绝任务。有没有办法解决这个问题?

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

4 个答案:

答案 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的解决方案很好。

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