ThreadPoolExecutor修复了具有自定义行为的线程池

时间:2012-11-08 10:47:16

标签: java multithreading threadpool threadpoolexecutor

我是这个主题的新手...我正在使用用Executors.newFixedThreadPool(10)创建的ThreadPoolExecutor,并且在池已满后我开始得到RejectedExecutionException。 有没有办法“强制”执行程序将新任务置于“等待”状态而不是拒绝它并在释放池时启动它?

由于

有关此问题 https://github.com/evilsocket/dsploit/issues/159

涉及的代码行https://github.com/evilsocket/dsploit/blob/master/src/it/evilsocket/dsploit/net/NetworkDiscovery.java#L150

4 个答案:

答案 0 :(得分:22)

如果使用Executors.newFixedThreadPool(10);,它会对任务进行排队,并等待线程准备就绪。

此方法

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

正如您所看到的,使用的队列是无限制的(这可能是一个问题),但这意味着队列永远不会填满,您永远不会被拒绝。

BTW:如果你有CPU绑定任务,最佳线程数可以是

int processors = Runtime.getRuntime().availableProcessors();
ExecutorService es = Executors.newFixedThreadPool(processors);

可能说明情况的测试类

public static void main(String... args) {
    ExecutorService es = Executors.newFixedThreadPool(2);
    for (int i = 0; i < 1000 * 1000; i++)
        es.submit(new SleepOneSecond());

    System.out.println("Queue length " + ((ThreadPoolExecutor) es).getQueue().size());
    es.shutdown();
    System.out.println("After shutdown");
    try {
        es.submit(new SleepOneSecond());
    } catch (Exception e) {
        e.printStackTrace(System.out);
    }
}

static class SleepOneSecond implements Callable<Void> {
    @Override
    public Void call() throws Exception {
        Thread.sleep(1000);
        return null;
    }
}

打印

Queue length 999998
After shutdown
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@e026161 rejected from java.util.concurrent.ThreadPoolExecutor@3e472e76[Shutting down, pool size = 2, active threads = 2, queued tasks = 999998, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2013)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:816)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1337)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:132)
    at Main.main(Main.java:17)

答案 1 :(得分:4)

线程调用exit很可能会将mStopped设置为false并关闭执行程序,但是:

  • 您正在运行的线程可能位于while (!mStopped)循环的中间,并尝试将任务提交给已被exit
  • 关闭的执行程序
  • while中的条件返回true,因为对mStopped所做的更改不可见(您不会在该标志周围使用任何形式的同步)。

我建议:

  • make mStopped volatile
  • 处理执行程序在循环中间关闭的情况(例如,通过捕获RejectedExecutionException,或者可能更好:在while循环之后关闭执行程序而不是在退出方法中关闭它)。 / LI>

答案 2 :(得分:2)

根据之前的建议,您可以使用阻塞队列构建固定大小ThreadPoolExecutor。然后,如果您提供自己的RejectedExecutionHandler,它将任务添加到阻塞队列,它将按照描述进行操作。

以下是如何构建此类执行程序的示例:

int corePoolSize    = 10;
int maximumPoolSize = 10;
int keepAliveTime   =  0;
int maxWaitingTasks = 10;

ThreadPoolExecutor blockingThreadPoolExecutor = new ThreadPoolExecutor(
        corePoolSize, maximumPoolSize,
        keepAliveTime, TimeUnit.SECONDS,
        new ArrayBlockingQueue<Runnable>(maxWaitingTasks),
        new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                try {
                    executor.getQueue().put(r);
                } catch (InterruptedException e) {
                    throw new RuntimeException("Interrupted while submitting task", e);
                }
            }
        });

答案 3 :(得分:0)

如果我理解正确,您可以使用固定数量的线程创建ThreadPool,但您可能需要将更多任务提交给线程池。我会根据请求计算keepAliveTime并动态设置它。这样你就不会有RejectedExecutionException。

例如

long keepAliveTime =((applications.size()* 60)/ FIXED_NUM_OF_THREADS)* 1000; threadPoolExecutor.setKeepAliveTime(keepAliveTime,TimeUnit.MILLISECONDS);

其中,应用程序是每次都可能不同的任务集合。

如果你知道任务的平均时间,那应该可以解决你的问题。