ThreadPoolExecutor神秘地拒绝了runnables

时间:2017-03-21 08:27:14

标签: java threadpool

鉴于以下单元测试,有人可以向我解释为什么在某些时候ThreadPoolExecutor会拒绝任务吗?

 @Test
public void testRejectionBehavior() throws Exception {
    final AtomicLong count = new AtomicLong(0);
    final AtomicInteger activeThreads = new AtomicInteger(0);
    for (;;) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(20, 20,
                                               0L, TimeUnit.MILLISECONDS,
                                               new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());
        int prestarted = pool.prestartAllCoreThreads();
        pool.allowCoreThreadTimeOut(false);
        System.out.println("Prestarted #" + prestarted);
        for (int i = 0; i < 100; i++) {
            final int thisTasksActive = activeThreads.incrementAndGet();
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    long value = count.incrementAndGet();
                    if (value % 50 == 0) {
                        System.out.println("Execution #" + value + " / active: "  + thisTasksActive);
                    }
                    if (Thread.currentThread().getName().equals("main")) {
                        throw new IllegalStateException("Execution #" + value + " / active: "  + thisTasksActive);
                    }
                    activeThreads.decrementAndGet();
                }
            });
            Thread.sleep(5);
        }
    }
}

我的输出如下:

....
Execution #200 / active: 1
Prestarted #20

java.lang.IllegalStateException: Execution #201 / active: 1 / pool stats: java.util.concurrent.ThreadPoolExecutor@156643d4[Running, pool size = 20, active threads = 20, queued tasks = 0, completed tasks = 0]

正如您所看到的,它执行了大约200次执行,然后突然拒绝了新迭代的第一项任务。

2 个答案:

答案 0 :(得分:0)

好的,经过大量挖掘ThreadPoolExecutor后发现在创建ThreadPoolExecutor时使用给定的参数,它无法立即执行任务。

即使您调用pool.prestartAllCoreThreads();,实际上也存在竞争条件。您看,prestartAllCoreThreads()创建了实现ThreadPoolExecutor.Worker接口的新Runnable实例。实例化它们时,它们将其内部状态设置为-1,使它们显示为&#34;活动线程&#34;在toString()的{​​{1}}输出中。现在,在它们的构造函数中,Worker实例创建一个新的Thread并为此Thread设置为ThreadPoolExecutor。直到他们的Runnable方法实际上被新启动的线程调用,他们才将其状态设置为可用于接受任务并随后调用run()方法。

简而言之,当您拥有一个具有同步队列的workQueue.take()并预启动所有线程时,这些线程可能需要一段时间才能真正启动并阻塞ThreadPooExecutor状态。直到那时你才能提交任务而不会被拒绝执行。

答案 1 :(得分:-1)

您没有为执行程序提供适当的队列来存储任务。SynchronousQueue没有容量,甚至没有1.您填写线程池,然后您的下一个任务必须在主要运行thread就是这种情况下的正常行为。

SynchronousQueue是一个奇怪的野兽,我唯一一次看到它在SO上的代码中使用的是执行者。在诸如“为什么我的代码行为怪异”这样的问题中。你是怎么想到在这里使用SynchronousQueue的?