ScheduledThreadPoolExecutor how to cancel scheduled threads

Viewed 87

I have a web app which runs an interface for controlling an irrigation system. The system starts up to 10 or so threads, using a ScheduledThreadPoolExecutor.

I have found that when I close down the server, many of these threads continue to run. From the Tomcat catalina.out log:

org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [GH2] appears to have started a thread named [pool-2-thread-1] but has failed to stop it. This is very likely to create a memory leak.

The classes that start these threads are dotted around my app. In order to try to have some control over stopping them, I have a single ScheduledThreadPoolExecutor created by a ServletContextListener and passed into each of the classes that might start a task.

I have tried setting properties in the ScheduledThreadPoolExecutor that I assumed would close down all threads when contextDestroyed() is called in the listener:

public void contextInitialized(ServletContextEvent event) {
        executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10);
        executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
        executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
        executor.setRemoveOnCancelPolicy(true);
        event.getServletContext().setAttribute("executor", executor);

Here is a typical bit of code that schedules a thread:

public WeatherScheduler(ScheduledThreadPoolExecutor executor) {
        this.executor = executor;
        checkEveryHour();
    }

    public void checkEveryHour() {
        final Runnable weatherChecker = new Runnable() {
            public void run() {
                if (!Thread.interrupted()) {

                    if (Date_TimeUtils.isBetween7amAnd8pm()) {
                        weather = new Weather();
                        weather.getNewForecast();
                        IrrigationLogger.getInstance().logEvent("weather forecast: " + weather.toString());
                    } else
                        LOGGER.log(Level.INFO, "weatherChecker not called as isBetween7amAnd8pm() == false ");
                }
            }
        };
        // delay of a minute to allow for getting internet connection, repeat every hour
        executor.scheduleAtFixedRate(weatherChecker, 1, 60, TimeUnit.MINUTES);
    }

When the server is closed down, the contextDestroyed() method is called in the listener, and I have

executor.shutdownNow();

included in that method.

But still I get the message in the log file that threads have been started that can't be stopped.

What am I missing?

Thanks.

1 Answers

The way any ThreadExecutor tries to stop a currently running thread is by setting interrupt flag by invoking Thread class method interrupt() on that thread. It is responsibility of the running thread to detect that it was "asked" to finish, so the running thread should clean up and terminate once the signal received. So first in your runner code surround it with try-catch where you will catch InterruptedException and also in your runner code you suppose to invoke method Thread.currentThread().isInterrupted() and if it returns true clean up and finish the thread (same as in your catch statement)

Related