In Java, is there a way to check if Runnable is completed in single thread?

Viewed 2135

In Java, I need to get two parameters, Runnable and Delay in milliseconds, and need to run the Runnable in the delay time. This needs to be run in single thread, and if the method is invoked with different parameter values and the previous task has not finished, it should be kept in a queue.

public void runScheduledTask(Runnable task, long delay) {
   // ...
}

...

runScheduledTask(task1, 10); // at 00:00:00.000
runScheduledTask(task2, 10); // at 00:00:00.005

when the method is invoked with task2, task1 has not started/finished yet as there is delay 10, so task2 should be stored in queue. Is there a way that I can check if the task1 has completed in this case? As this should be run in the current thread, I have no idea what classes or tools I can use.

1 Answers

tl;dr

is there a way to check if Runnable is completed in single thread?

Yes.

  1. Run your Runnable with a ScheduledExecutorService provided by a call to Executors.newSingleThreadScheduledExecutor.
  2. Capture the returned ScheduledFuture object.
  3. Interrogate that ScheduledFuture object for its completion status, asking if it is done or is cancelled.

Executors framework

Modern Java offers the Executors framework to nicely manage spinning off tasks onto other threads. See tutorial by Oracle.

The Executors class (plural in name) has factory methods for producing an ExecutorService object.

You want a delay in execution rather than immediate execution. So than means you want a ScheduledExecutorService.

ScheduledExecutorService scheduledExecutorService = … 

You asked for single-threaded execution. That means you want an executor backed by a thread pool sized to a single thread rather than several.

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() ;

Tell that scheduled executor service to run your Runnable after your desired delay. Call the ScheduledExecutorService::schedule method.

If the executors framework were being written today, the delay would be represented as a java.time.Duration object. But java.time was not yet invented. So a delay is represented as some number along with a TimeUnit object to designate the granularity of time. You did not specify your desired granularity, so I'll go with seconds.

scheduledExecutorService.schedule( runnable , 10 , TimeUnit.SECONDS ); 

Put all that together.

Runnable runnable = ( ) -> {
    System.out.println( "Running the runnable at " + Instant.now() );
};

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.schedule( runnable , 10 , TimeUnit.SECONDS ); // Wait ten seconds before running the runnable.

// Wait long enough on this `main` thread for the background thread to do its thing.
try
{
    Thread.sleep( Duration.ofSeconds( 12 ).toMillis() ); 
}
catch ( InterruptedException e )
{
    e.printStackTrace();
}

// Clean-up.
scheduledExecutorService.shutdown();  // Always shutdown your executor service when no longer needed, or when ending your app.
System.out.println( "Done at " + Instant.now() );

Notice how we gracefully shutdown the executor service. Otherwise the thread(s) backing the executor might continuing running zombie-like indefinitely after our app has ended.

See this code run live at IdeOne.com.

Running the runnable at 2020-11-16T05:30:04.106661Z
Done at 2020-11-16T05:30:06.105624Z

Check for completion

You asked:

Is there a way that I can check if the task1 has completed

Yes.

The ScheduledExecutorService::schedule method seen above returns a ScheduledFuture object. We can interrogate that returned ScheduledFuture object for its completion status.

Our code above ignores that returned object. Let's alter our code to capture the returned object.

Runnable runnable = ( ) -> {
    System.out.println( "Running the runnable at " + Instant.now() );
};

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture future = scheduledExecutorService.schedule( runnable , 10 , TimeUnit.SECONDS ); // Wait ten seconds before running the runnable.

try
{
    Thread.sleep( Duration.ofSeconds( 4 ).toMillis() ); // Wait a short while, then see if runnable completed.
    System.out.println( "isDone: " + future.isDone() + " | " + "isCancelled: " + future.isCancelled() );
    Thread.sleep( Duration.ofSeconds( 4 ).toMillis() ); // Wait a short while, then see if runnable completed.
    System.out.println( "isDone: " + future.isDone() + " | " + "isCancelled: " + future.isCancelled() );
    Thread.sleep( Duration.ofSeconds( 4 ).toMillis() ); // Wait a short while, then see if runnable completed.
    System.out.println( "isDone: " + future.isDone() + " | " + "isCancelled: " + future.isCancelled() );
}
catch ( InterruptedException e )
{
    e.printStackTrace();
}

// Clean-up.
scheduledExecutorService.shutdown();
System.out.println( "Done at " + Instant.now() );

When run.

isDone: false | isCancelled: false
isDone: false | isCancelled: false
Running the runnable at 2020-11-16T05:44:07.803125Z
isDone: true | isCancelled: false
Done at 2020-11-16T05:44:09.821267Z
Related