创建动态(增长/缩小)线程池

时间:2012-06-28 16:49:15

标签: java multithreading threadpool

我需要在Java中实现一个线程池(java.util.concurrent),当空闲时,线程数量处于某个最小值,当作业提交到线程时,它会增长到上限(但不会更远)他们完成执行,并在完成所有工作并且不再提交任何工作时收缩回到下限。

你会如何实现这样的东西?我想这将是一个相当常见的使用场景,但显然java.util.concurrent.Executors工厂方法只能创建固定大小的池和池,当提交许多作业时,这些池和池无限增长。 ThreadPoolExecutor类提供corePoolSizemaximumPoolSize参数,但其文档似乎暗示同时拥有超过corePoolSize个线程的唯一方法是使用有限的工作队列,在这种情况下,如果你已经到达maximumPoolSize个线程,你会得到拒绝工作,你必须自己处理?我想出了这个:

//pool creation
ExecutorService pool = new ThreadPoolExecutor(minSize, maxSize, 500, TimeUnit.MILLISECONDS,
    new ArrayBlockingQueue<Runnable>(minSize));
...

//submitting jobs
for (Runnable job : ...) {
    while (true) {
        try {
            pool.submit(job);
            System.out.println("Job " + job + ": submitted");
            break;
        } catch (RejectedExecutionException e) {
            // maxSize jobs executing concurrently atm.; re-submit new job after short wait
            System.out.println("Job " + job + ": rejected...");
            try {
                Thread.sleep(300);
            } catch (InterruptedException e1) {
            }
        }
    }
}

我忽略了什么吗?有一个更好的方法吗?此外,根据一个人的要求,上述代码至少(我认为)(total number of jobs) - maxSize工作完成后才会完成,这可能会有问题。因此,如果您希望能够将任意数量的作业提交到池中并立即继续而无需等待其中任何一个完成,我看不出如何在没有专门的“作业总结”线程的情况下执行此操作保存所有已提交作业所需的无限队列。 AFAICS,如果你为ThreadPoolExecutor本身使用一个无界的队列,它的线程数将永远不会超过corePoolSize。

3 个答案:

答案 0 :(得分:9)

当增长和缩小与线程一起出现时,我只想到一个名字:来自java.util.concurrent 包的CachedThreadPool。

ExecutorService executor = Executors.newCachedThreadPool();

CachedThreadPool()可以重用线程,以及在需要时创建新线程。 是的,如果一个线程闲置60秒,CachedThreadPool将会终止它。所以这很轻量级 - 用你的话来增长和缩小!

答案 1 :(得分:4)

可能对您有帮助的一个技巧是分配使用相同线程的RejectedExecutionHandler将作业提交到阻塞队列。这将阻止当前线程并消除对某种循环的需要。

请在此处查看我的回答:

  

How can I make ThreadPoolExecutor command wait if there's too much data it needs to work on?

这是从该答案中复制的拒绝处理程序。

final BlockingQueue queue = new ArrayBlockingQueue<Runnable>(200);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(nThreads, nThreads,
       0L, TimeUnit.MILLISECONDS, queue);
// by default (unfortunately) the ThreadPoolExecutor will call the rejected
// handler when you submit the 201st job, to have it block you do:
threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
   public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
      // this will block if the queue is full
      executor.getQueue().put(r);
   }
});

只要您意识到在核心线程之上创建任何线程之前填充 first 的有界阻塞队列,就应该能够使用核心/最大线程数。因此,如果您有10个核心线程并且您希望第11个作业启动第11个线程,那么您将需要一个大小为0的阻塞队列(可能是SynchronousQueue)。我觉得这是一个非常好的ExecutorService类的真正限制。

答案 2 :(得分:1)

maximumPoolSize设为Integer.MAX_VALUE。如果你有超过20亿个线程......那么,祝你好运。

无论如何,ThreadPoolExecutor的Javadoc声明:

  

通过将maximumPoolSize设置为基本无限制的值(例如Integer.MAX_VALUE),可以允许池容纳任意数量的并发任务。最典型的情况是,核心和最大池大小仅在构造时设置,但也可以使用setCorePoolSize(int)和setMaximumPoolSize(int)动态更改。

使用类似于LinkedBlockingQueue的类似无界任务队列,这应该具有任意大容量。