1
votes

In my program most of the time tasks are rarely submitted to the executor, yet they don't cease completely. There are periodic bursts when many tasks are submitted at once.

Even though allowCoreThreadTimeOut is set and only one thread would be enough most of the time, the redundant executor threads don't stop.

This is because of the fairness of the executor's blocking queue: when multiple threads wait for it, all have equal chance to get a task and their idle time doesn't grow significantly.

Is there a workaround? For example, a queue that in case of multiple waiting threads returns in the thread with lowest id?

public class ShrinkTPE {

    public static void main(final String[] args) throws Exception {
        final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors
                .newFixedThreadPool(NTHREADS);
        executor.setKeepAliveTime(ALIVE_TIME, TimeUnit.SECONDS);
        executor.allowCoreThreadTimeOut(true);

        // thread alive time is 10s
        // load all threads with tasks at start and every 12s
        // also submit one task each second
        for (int i = 0;; i++) {
            int j = 0;
            do {
                if (false && !mostThreadsUnused(i))
                    break;
                final int i2 = i, j2 = j;
                executor.submit(new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        System.out.println(""
                                + Thread.currentThread().getName() + " " + i2
                                + " " + j2);
                        Thread.sleep(300);
                        return null;
                    }
                });
            } while (mostThreadsUnused(i) && ++j < NTHREADS);
            Thread.sleep(1000);
            System.out.println();
        }
    }

    private static boolean mostThreadsUnused(final int i) {
        return i % (ALIVE_TIME + 2) == 0;
    }

    private static final int NTHREADS = 5;

    private static final int ALIVE_TIME = 10;
}
2
If you want idle threads to be reclaimed, maybe you should use a cached thread pool instead of a fixed thread pool? Try Executors.newCachedThreadPool()? - clay
A cached thread pool will terminate unused threads after 60 seconds. I'm sure there is some way to configure that. Is that the behavior you are looking for? - clay
@clay cached thread pool has unlimited maximumPoolSize and no real queue, which is bad - basin

2 Answers

1
votes
final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(N_THREAD);

You are using fixedThreadPool and that means, that pool will have N_THREAD number of threads constantly all the time. allowCoreThreadTimeout is neglected here.

Use different thread pool, perhaps CachedThreadPool? It will reuse existing threads, but it will spin up additional threads if you submit new task to the pool and there will be no idle thread.

Idle threads dies after X amount of time (default 60 seconds of idle)

0
votes

The official JDK implementation of newCachedThreadPool is as follows. You can simply call that constructor directly if you want to set a maximum thread pool size or customized the keepAliveTime or use a different queue.

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}