如果挂起另一个计时器事件,则 ScheduledThreadPoolExecutor 计时器不会触发

ScheduledThreadPoolExecutor timer doesn't fire if another timer event is hanged

考虑以下代码。

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class Test {
    private static ScheduledThreadPoolExecutor threadPool = new ScheduledThreadPoolExecutor(0);

    public static void onTimer() {
        System.out.println("onTimer");
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignored) {
            }
        }
    }

    public static void onTimer2() {
        System.out.println("onTimer2");
    }

    public static void main(String[] args) {
        threadPool.scheduleAtFixedRate(Test::onTimer, 0, 500, TimeUnit.MILLISECONDS);
        threadPool.scheduleAtFixedRate(Test::onTimer2, 0, 500, TimeUnit.MILLISECONDS);
    }
}

有两个计时器以固定速率调度,在第一个计时器事件中,任务无限期挂起。

即使第一个任务挂起,我希望第二个计时器以指定的速率触发,因为它是使用允许创建任意数量的线程的线程池创建的。

但是,第二个计时器永远不会触发。

有人可以解释为什么吗?

ScheduledThreadPoolExecutor's javadoc 的引述解释了我的猜测

While this class inherits from ThreadPoolExecutor, a few of the inherited tuning methods are not useful for it. In particular, because it acts as a fixed-sized pool using corePoolSize threads and an unbounded queue, adjustments to maximumPoolSize have no useful effect. Additionally, it is almost never a good idea to set corePoolSize to zero or use allowCoreThreadTimeOut because this may leave the pool without threads to handle tasks once they become eligible to run.

您错过的关键部分是 ScheduledThreadPoolExecutor class 是“一个固定大小的池 使用 corePoolSize线程和无界队列”。因此它不会产生超过 corePoolSize.

的任何额外线程

如果您想知道为什么它实际上在 corePoolSize == 0 时执行,这里是 ThreadPoolExecutor 的一段代码片段,它确保至少有一个线程(并且在您的情况下阻止更多线程启动的线程):

call order:  
1. java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate 
2. java.util.concurrent.ScheduledThreadPoolExecutor#delayedExecute   
3. java.util.concurrent.ThreadPoolExecutor#ensurePrestart
            /**
             * Same as prestartCoreThread except arranges that at least one
             * thread is started even if corePoolSize is 0.
             */
            void ensurePrestart() {
                int wc = workerCountOf(ctl.get());
                if (wc < corePoolSize)
                    addWorker(null, true);
                else if (wc == 0) //here's your case
                    addWorker(null, false);
            }