简体   繁体   中英

ScheduledExecutorService running many threads when I created it using Executors.newSingleThreadScheduledExecutor

Context:

  • This is a Jersey application
  • Run on a Tomcat server
  • Connects to a MySQL database using Hibernate
  • All on Netbeans

I'm creating a ScheduledExecutorService to run everyday at a particular time. However, when it runs it creates many connections to the database. I imagine it's because the threads aren't being destroyed when I rerun the application, so many ScheduledExecutorService s build up in the JVM and all execute at the same time. But, I'm not sure what the cause is.

I create the ScheduledExecutorService with a ThreadFactory that only creates daemon threads, so when I rerun the application I imagine that all daemon threads should be destroyed so that only one ScheduledExecutorService ever exists.

Here is the creation of the ScheduledExecutorService :

ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = Executors.defaultThreadFactory().newThread(r);
            t.setDaemon(true);
            return t;
        }
    });

Here is how it runs everyday at a particular time:

public void startExecutionAt(int targetHour, int targetMin, int targetSec){
        long delay = computeNextDelay(targetHour, targetMin, targetSec);

        mExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                Thread t = new Thread(new CustomRunnable());
                t.setDaemon(false);
                t.start();
                startExecutionAt(targetHour, targetMin, targetSec);
            }
        }, delay, TimeUnit.SECONDS);
    }

I know it's creating many connections to MySQL because in the P6Spy log there are many many connections and queries made when CustomRunnable is executed when CustomRunnable only runs one Hibernate query that selects 5 records from a table.

Any ideas as to what could be going on?

You obviously misuse your ScheduledExecutorService , here is what you are supposed to do:

public void startExecutionAt(int targetHour, int targetMin, int targetSec){
    long delay = computeNextDelay(targetHour, targetMin, targetSec);

    mExecutorService.scheduleAtFixedRate(
        new CustomRunnable(), 
        1000L * delay, 
        TimeUnit.DAYS.toMillis(1), 
        TimeUnit.MILLISECONDS
    );
}

You call this method once for all and it will launch your task with delay as initial delay then every day at the same exact time.

More details here .

Indeed you currently create a new thread any time the task is executed then call again the method to schedule the next execution which is not what you are supposed to do, the task is already executed in a pool no need to create a new Thread moreover no need to schedule again manually when you can do it directly with the ScheduledExecutorService .

Response Update:

As the scheduling seems to be more complicated in your case, here is how you should do it instead:

public void startExecutionAt(int targetHour, int targetMin, int targetSec){
    long delay = computeNextDelay(targetHour, targetMin, targetSec);

    mExecutorService.schedule(new Runnable() {
        @Override
        public void run() {
            try {
                new CustomRunnable().run();
            } finally {
                startExecutionAt(targetHour, targetMin, targetSec);
            }
        }
    }, delay, TimeUnit.SECONDS);
}

This way you won't create and start new threads for nothing.

The second part of your problem should be fixed by starting and shutting down properly your ScheduledExecutorService respectively on ServletContext initialized and destroyed, in other words the class that manages your scheduler should be a ServletContextListener .

public class MyServletContextListener implements ServletContextListener {
    private ScheduledExecutorService executorService;
    public void contextInitialized(ServletContextEvent sce) {
        executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = Executors.defaultThreadFactory().newThread(r);
                t.setDaemon(true);
                return t;
            }
        });
    }

    public void contextDestroyed(ServletContextEvent sce) {
        executorService.shutdownNow();
    }

    public void startExecutionAt(int targetHour, int targetMin, int targetSec){
        // code defined above here
    }
}

If you need services to make it work, use CDI to get them by injection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM