Search code examples
javadiscord-jda

Toggle ScheduledExecutorService so its not always running


I'm currently working on a discord bot just to mess around with, but have used SES in the past for other things, but I always struggle to disable/enable the SES, calling ses.shutdown(); does not work and the runnable continues, and if I did shutdown im not sure how to restart it.

Here is my current code...

private final ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

protected void startRainbow(Role rle) {
    roleRainbow[] colors = roleRainbow.values();
    HashMap<String, Integer> map = new HashMap<>();
    map.put("loop", 0);

    Runnable r = () -> {

        if(map.get("loop") >= colors.length) {
            map.put("loop", 0);
        }



        rle.getManager().setColor(Color.decode(colors[map.get("loop")].toString())).queue();
        System.out.println("color is: " + colors[map.get("loop")].toString());
        System.out.println("color from role: " + rle.getColor());

        map.put("loop", map.get("loop") + 1);
    };

    ses.scheduleAtFixedRate(r, 1L, 1, TimeUnit.SECONDS);
}

Solution

  • There are two angles to this problem:

    • Interrupting a currently-running thread.
    • Graceful shut down of a scheduled executor service.

    The first has been addressed many many times on Stack Overflow already. In brief: You must signal a thread that you want it to interrupt itself. That thread's task must be coded is such a way to look for the interruption signal.

    For the second, there are two steps involved:

    • You first tell the scheduled executor service that you want to stop any further executions of future-scheduled tasks. We do this with a call to shutdown. This call does not interrupt any currently existing task (see above paragraph).
      • Alternatively, you may call shutdownNow. This call may signal the running task to interrupt, but no guarantees. The behavior is implementation-defined.
    • After requesting the service to stop further scheduling, you can watch to see if the service does indeed stop after a reasonable amount of time has passed. We do this with a call to awaitTermination.
    ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor() ;
    ses.scheduleAtFixedRate( task , initialDelay , period , timeUnit ) ;
    …
    ses.shutdown() ;
    boolean terminated = false ;
    try { terminated  = ses.awaitTermination( 1 , TimeUnit.MINUTES ); } catch ( InterruptedException e ) { e.printStackTrace(); }
    if( ! terminated ) { … Houston, we have a problem. … }
    

    The call to shutdown (and shutdownNow) does not block; it does not wait for anything to happen, and does not wait for tasks to end.

    The call to awaitTermination does block until either:

    • Scheduled executor service finishes its last running task, and ends.
    • The specified time-out elapses.

    Test for the boolean returned by the awaitTermination call. To quote the Javadoc:

    true if this executor terminated and false if the timeout elapsed before termination

    You asked:

    if I did shutdown im not sure how to restart it.

    Once shutdown, the scheduled executor service is done. To schedule tasks for execution, you must instantiate a new scheduled executor service.

    Be aware that a scheduled executor service with no tasks to execute takes very little memory or CPU. So no need to shutdown if you might later need the service.

    However, be sure to always shutdown your executor services before your app ends. Otherwise the backing thread pool(s) may continue running indefinitely, like a zombie 🧟‍♂️.