Shutdown executor without knowing when it will finish all the tasks

85 Views Asked by At

I have a task that should wait for a condition (OpenCms startup) and then notify some listeners. to do this I used an ExecutorService:

public void check(final ExecutorService executorService) {
    executorService.submit(() -> {
        waitForInitialization();
        notifyListeners();
    });
}


private void waitForInitialization() {
        while (OpenCms.getRunLevel() < OpenCms.RUNLEVEL_4_SERVLET_ACCESS) {
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

the question is how to shutdown this executor. I could use awaitTermination but I should provide a timeout which I don't know exactly. it could vary from one environment to the other.

2

There are 2 best solutions below

2
bsaverino On

Based on the provided information, I'd clearly recommend an event-based approach. Especially knowing that in your own code there is a call like notifyListeners(). In fact, that's the way to go.

In summary, once the precondition is met somewhere in your app, just notify the listeners of this event. In your example, the "OpenCms run level" change is typically an event. So, just go for an Observer pattern, or a pub-sub model to observe or monitor these changes.

If you modify your approach, you will not have to worry about the waiting time around the initialization, except if you wish to handle the absence of event specifically. That would be done again after some timeout, but with the advantage of not blocking an executor thread.

0
Gray On

the question is how to shutdown this executor. I could use awaitTermination but I should provide a timeout which I don't know exactly. it could vary from one environment to the other.

The question I would ask is do you need a timeout at all? Often if you know that a particular job will finish at some point I just wait for a timeout Long.MAX_VALUE – effectively forever. Other times I'll do something like:

  1. threadPool.shutdown();
  2. threadPool.awaitTermination(...) of some small value (maybe 10 seconds)
  3. threadPool.shutdownNow(); to interrupt the threads
  4. threadPool.awaitTermination(...); of Long.MAX_VALUE because I know the jobs will finish eventually

it could vary from one environment to the other.

If it could vary then maybe you should be able to calculate what a proper timeout would be for each environment?

Lastly, don't be afraid of passing in a ThreadFactory that creates daemon threads. For some jobs I shutdown() the thread-pool but never wait for them to complete because I don't care about their status so I create the threads in the pool with daemon enabled maybe using something like the following thread-factory.

/** Thread factory which sets name and optionally daemon */
public class PoolNameThreadFactory implements ThreadFactory {

    private final String poolName;
    private final Boolean daemon;
    private final AtomicInteger threadNum = new AtomicInteger(0);

    public PoolNameThreadFactory(String poolName) {
        this(poolName, null);
    }

    public PoolNameThreadFactory(String poolName, boolean daemon) {
        this(poolName, (Boolean) daemon);
    }

    private PoolNameThreadFactory(String poolName, Boolean daemon) {
        this.poolName = poolName;
        this.daemon = daemon;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setName(poolName + '-' + threadNum.incrementAndGet());
        if (daemon != null) {
            thread.setDaemon(daemon);
        }
        return thread;
    }
}