Olavi Mustanoja
Olavi Mustanoja

Reputation: 2065

ScheduledExecutorService - program not ending after one-shot action

I have a scheduled task in my program that closes a frame after a given period of time. However, after the task has been executed, the program keeps running as if the ScheduledExecutorService was still running on a different thread.

This is the relevant part of my code:

int delay = 1000;

ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
ex.schedule(() -> {

    System.out.println("executed");
    getWindow().closeWindow();
    // ex.shutdown();

}, delay, TimeUnit.MILLISECONDS);

Here the task is executed after a 1 second delay, "executed" is printed once, the frame closes, and the program keeps running even after this code. If I uncomment the ex.shutdownNow();, the program successfully ends as intended. However, I cannot figure out why this is happening. I also failed to find anything from the rest of the Internet.

MCVE:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args) {
        int delay = 1000;

        ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
        ex.schedule(() -> {

            System.out.println("executed");
            // ex.shutdown();

        }, delay, TimeUnit.MILLISECONDS);
    }

}

The lambdas might've given it away, but this is indeed Java 8.

Why is the program not stopping after the task has been executed?

Upvotes: 6

Views: 7792

Answers (5)

Ravi Yadav
Ravi Yadav

Reputation: 2386

I am starting scheduler from onCreate() and stopping it in onDestroy() approach to stop the scheduler service.

public MyActivity extends Activity
{
ScheduledExecutorService scheduledExecutorService;
    ScheduledFuture<?> scheduledFuture;
    private int apiThreshold = 10;//seconds


onCreate()
{
    startScheduler();
 }

onDestroy()
{
    if (scheduledFuture != null) 
{ 
 stopScheduler(); 
}
 shutDownService();
  super.onDestroy();
 }

 public void startScheduler() {
        Debug.e(TAG, "inside start scheduler");
        scheduledExecutorService = Executors.newScheduledThreadPool(1);

        scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                // call method to do your task/perform your repeated task
            }
        }, 4, apiThreshold, TimeUnit.SECONDS);
    }


public void shutDownService()
    {
        if (scheduledExecutorService != null) {
            Log.e(“test,"in shutDown service close if not null");
            scheduledExecutorService.shutdownNow(); // shutdown will allow the final iteration to finish
            // executing where shutdownNow() will kill it immediately

            Log.e(“test,"is service shutdown(true/false)=="+scheduledExecutorService.isShutdown());
        }
    }

 }

Upvotes: 0

Aureliano
Aureliano

Reputation: 11

You can call shutdown from ScheduledExecutorService as it will wait for thread execution and then finalize thread pool. As you can see in Javadoc: "Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down."

Example:

...
scheduledExecutorService.schedule(runnable, delay, TimeUnit.MILLISECONDS);
scheduledExecutorService.shutdown();
...

Upvotes: 1

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

I would approach this entirely differently. You state:

I have a scheduled task in my program that closes a frame after a given period of time.

Why not instead use a Swing Timer for this as this was built to work well with the Swing event thread?

new Timer(1000, new ActionListener() {

    public void actionPerformed(ActionEvent e) {
        ((Timer) e.getSource()).stop();
        someWindow.dispose();        
    }

}).start();

Upvotes: 1

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279970

The ScheduledExecutorService thread pool returned by Executors#newSingleThreadScheduledExecutor() uses non daemon threads. Until you shut down the thread pool, these are still alive awaiting tasks. A JVM does not end while non-daemon threads are alive.

You can use the overloaded Executors#newSingleThreadScheduledExecutor(ThreadFactory) and provide your own ThreadFactory implementation which creates daemon threads. Note that this risks the case where your task may not even run because the JVM would exit before the task's scheduled time.

Do as you've discovered and shut it down. Note that you should shut always it down somewhere safe, where the operation can't fail.

Upvotes: 14

gknicker
gknicker

Reputation: 5569

The Java Virtual Machine runs until all threads that are not daemon threads have died. And Executors.defaultThreadFactory() creates each new thread as a non-daemon thread. However, there is an overload of Executors.newSingleThreadScheduledExecutor(); which takes a ThreadFactory as a parameter, if you care to venture in that direction.

    public static void main(String[] args) {
        int delay = 1000;

        class DaemonFactory implements ThreadFactory
        {
            @Override
            public Thread newThread(Runnable r)
            {
                Thread t = new Thread(r);
                t.setDaemon(true);
                return t;
            }
        }

        ThreadFactory tf = new DaemonFactory();
        ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor(tf);
        ex.schedule(() -> {
            System.out.println("executed");
        }, delay, TimeUnit.MILLISECONDS);
    }

Upvotes: 4

Related Questions