I'm trying to make a ScheduledExecutorService where only one task is active at a time and only once a task has finished, the next task will begin its delay with an arbitrary delay amount.
As a very simple example of what I mean, take a look at this method. The idea is to schedule 10 Runnables to simulate a countdown from 10-1. Each interval takes one second (imagine this was an arbitrary amount of seconds though, I can't use scheduleAtFixedRate in my use case).
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public void startCountdown() {
for (int i = 10; i > 0; i--) {
int countdownNumber = i;
scheduler.schedule(() -> {
System.out.println(countdownNumber);
}, 1, TimeUnit.SECONDS);
}
}
However, this will simply print all 10 numbers at once, instead of waiting for a second between each value. The only way I can circumvent this (to my knowledge) is calculating the ABSOLUTE delay, as opposed to the relative one.
While it's possible to calculate the absolute time for each item, it would be quite a hassle. Isn't there some construct in Java that allows me to queue many items at once, but waits in between each item for the delay to finish, rather than processing every delay at once?
tl;dr
Do not use your countdown number to directly schedule your tasks. Have one number for scheduling number of seconds to wait (1,2,3,…) and another number for the countdown (9,8,7,…).
Use scheduleAtFixedRate to schedule your tasks for an increasing number of seconds. No need for executor service to be single-threaded.
Details
Task that re-schedules itself
Isn't there some construct in Java that allows me to queue many items at once, but waits in between each item for the delay to finish, rather than processing every delay at once?
If you have an arbitrary amount of time not known up front when beginning the scheduling, then you should only run one task at a time. Let the task re-schedule itself.
To enable a task to re-schedule itself, pass a reference to the ScheduledExecutorService to the task object (your Runnable or Callable) as an argument in the constructor. After the task completes its main work, it discovers/calculates the amount of time to elapse for the next run. The task then submits itself (this) to the passed executor service, along with the amount of time to elapse before the next task execution.
I have already posted Answers on Stack Overflow with code for tasks that re-schedule themselves. I would expect others have as well. Search to learn more.
Regarding the "countdown" aspect of your Question, read on.
Countdown
You have the right approach in using a scheduled executor service. The problem is that you are calling the wrong method on that class.
Your call to schedule means you are scheduling several tasks to all run after a single second. All those tasks are starting from the moment your call is made. So each runs after one second from your call to schedule. So the ten tasks are all waiting a second from almost the same moment: ten moments a split-second apart, the split-second being the time it takes for your for loop to continue.
scheduleAtFixedRate
The method you are looking for is scheduleAtFixedRate. To quote the doc:
Submits a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is, executions will commence after initialDelay, then initialDelay + period, then initialDelay + 2 * period, and so on.
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public void countdown( ScheduledExecutorService scheduler )
{
for ( int i = 1 ; i <= 10 ; i++ )
{
int countdownNumber = 10 - i ; // For 9 through 0. Add 1 for 10 through 1.
scheduler.scheduleAtFixedRate
(
() -> { System.out.println( countdownNumber ) ; } ,
i , // 1 second, then 2 seconds, then 3 seconds, and so on to 10 seconds.
TimeUnit.SECONDS
) ;
}
}
… Eventually shut down your scheduled executor service.
Notice how this approach does not require the ScheduledExecutorService to be single-threaded.
Full example
Here is a complete example app.
package work.basil.example.countdown;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Countdown
{
public static void main ( String[] args )
{
Countdown app = new Countdown();
app.demo();
}
private void demo ( )
{
System.out.println( "INFO - Demo start. " + Instant.now() );
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // Our code does *not* require the executor service to be single-threaded. But for this particular example, we might as well do it that way.
this.countdown( scheduler );
this.shutdownAndAwaitTermination( scheduler , Duration.ofMinutes( 1 ) , Duration.ofMinutes( 1 ) );
System.out.println( "INFO - Demo end. " + Instant.now() );
}
public void countdown ( final ScheduledExecutorService scheduler )
{
Objects.requireNonNull( scheduler ) ;
for ( int i = 1 ; i <= 10 ; i++ )
{
int countdownNumber = 10 - i; // For 9 through 0. Add 1 for 10 through 1.
scheduler.scheduleAtFixedRate
(
( ) -> { System.out.println( "Countdown: " + countdownNumber + " at " + Instant.now() ); } ,
i , // 1 second, then 2 seconds, then 3 seconds, and so on to 10 seconds.
TimeUnit.SECONDS
);
}
}
// My slightly modified version of boilerplate code taken from Javadoc of `ExecutorService`.
// https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html
void shutdownAndAwaitTermination ( final ExecutorService executorService , final Duration waitForWork , final Duration waitForRemainingTasks )
{
Objects.requireNonNull( executorService ) ;
Objects.requireNonNull( waitForWork ) ;
Objects.requireNonNull( waitForRemainingTasks ) ;
executorService.shutdown(); // Disable new tasks from being submitted
try
{
// Wait a while for existing tasks to terminate
if ( ! executorService.awaitTermination( waitForWork.toMillis() , TimeUnit.MILLISECONDS ) )
{
executorService.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if ( ! executorService.awaitTermination( waitForRemainingTasks.toMillis() , TimeUnit.MILLISECONDS ) )
{ System.err.println( "ExecutorService did not terminate." ); }
}
}
catch ( InterruptedException ex )
{
// (Re-)Cancel if current thread also interrupted
executorService.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
System.out.println( "DEBUG - shutdownAndAwaitTermination ran. " + Instant.now() );
}
}
When run:
INFO - Demo start. 2023-01-20T21:24:47.379244Z
Countdown: 9 at 2023-01-20T21:24:48.390269Z
Countdown: 8 at 2023-01-20T21:24:49.390045Z
Countdown: 7 at 2023-01-20T21:24:50.389957Z
Countdown: 6 at 2023-01-20T21:24:51.386468Z
Countdown: 5 at 2023-01-20T21:24:52.390168Z
Countdown: 4 at 2023-01-20T21:24:53.386538Z
Countdown: 3 at 2023-01-20T21:24:54.387583Z
Countdown: 2 at 2023-01-20T21:24:55.386705Z
Countdown: 1 at 2023-01-20T21:24:56.389490Z
Countdown: 0 at 2023-01-20T21:24:57.387566Z
DEBUG - shutdownAndAwaitTermination ran. 2023-01-20T21:24:57.391224Z
INFO - Demo end. 2023-01-20T21:24:57.391966Z
By the way, know that scheduled tasks do not always fire exactly on time for a variety of reasons.
Also, be aware that messages sent to System.out across threads do not always appear on the console chronologically. If you care about order, always include and study a timestamp such as Instant#now.
You can schedule the next call inside the task.
void countdown(final int i) {
scheduler.schedule(() -> {
System.out.println(i);
if (i > 0) countdown(i - 1);
}, 1, TimeUnit.SECONDS);
}
// ...
countdown(10);
Related
How can I simulate or write a code that would indicate that Java blocks a function till it has finished its execution.
This way I will be able to show that Java has blocking I/O.
What I thought as my initial solution was to make an infinite loop but that didn't work as it will never finish its execution.
my other solution was to make a REST API and in that get request would delay and return something and think this might work but is there a native way to do it?
Here is the Java code below I want to delay the method fun2() without creating a new thread.
public class SetTimeOut {
public static void fun1(String str){
System.out.println(str);
}
public static void fun2(String str){
//how to make this function wait for 3 sec?
System.out.println(str);
}
public static void fun3(String str){
System.out.println(str);
}
public static void main(String[] args) {
fun1("Hello from fun1 is being called");
fun2("Hello from fun2 is being called");
fun3("Hello from fun3 is being called");
}
}
Here is an equivalent JavaScript code to show that JavaScript has a non-blocking I/O. Want to simulate a similar kind of behavior in Java.
console.log("Hey");
setTimeout(() => {
console.log("there!")
},3000);
console.log("please help");
just want to write something similar in java but it should block till the execution of the setTimeout() function is complete.
tl;dr
You can pause execution of a thread.
Thread
.sleep(
Duration.ofSeconds ( 7 )
)
Sleep
As discussed in comments, you can sleep the thread for a specific length of time. The static method Thread.sleep method pauses execution of the current thread.
See Pausing Execution with Sleep in The Java Tutorials by Oracle Corp.
Thread.sleep( Duration.of… ( … ) ) ;
For example, sleep a half second.
Thread.sleep( Duration.ofMillis ( 500 ) ) ; // A half-second.
Or seven seconds.
Thread.sleep( Duration.ofSeconds ( 7 ) ) ; // Seven seconds.
Or half a day.
Thread.sleep( Duration.ofHours ( 12 ) ) ; // Twelve hours.
Prior to Java 19
Before Java 19+, you must pass a mere int rather than a Duration, as a count of milliseconds.
For example, here we pause for a half-second.
Thread.sleep( 500 ) ; // 500 milliseconds is a half-second.
In Java 8 through Java 18, no need for you to do the math to get milliseconds. Use Duration#toMillis.
Thread.sleep( Duration.ofMinutes( 1 ).plusSeconds( 30 ).toMillis() ) ; // 1.5 minutes as a count of milliseconds.
Let's say I have a task that will check if a certain condition has been met. If that condition has been met, I should terminate the task early. However, I should continue to run the task for N number of minutes while continuously checking if that condition has been met. At the end of N minutes, the task should terminate itself.
I've been looking into Timer, TimerTask, and ExecutorService, but none of them seem to offer the type of solution I am looking for. For example, these approaches will allow you to schedule a task to run once, or repeatedly, but not for a specific amount of time (aka N minutes).
ScheduledExecutorService can accomplish this.
What you need to do:
Schedule the block of work you want to do at a fixed interval (in time units, so like seconds, minutes, hours, etc)
When you've completed the work, execute ScheduledExecutorService#shutdownNow
To time out after a period of time, use ScheduledExecutorService#awaitTermination to delay the termination of the scheduled task until your threshold elapses
Task reschedules itself
The Answer by Makato is right about suggesting the use of an executor service. But ending the scheduled executor service as the way to end your repeating task is less than optimal. Often we have one executor service handling many tasks. So while we want our repeating task to end, the executor service may need to continue scheduling other tasks to run.
Instead, ironically, 👉🏻 do not schedule the repeating task to repeat. Schedule the task to run once. Have the task check its status.
If the status requires the task run again, have the task schedule itself on the executor service for another run.
If the status is complete, and no further executions of that task are needed, simply opt out of another submission to the executor service.
The key to making this work is passing a reference to the scheduled executor service to the constructor of your task. The task keeps that reference as a member field of its own class. So the task can use that reference to pass itself to the scheduled executor service for another submission. The task becomes self-scheduling.
Here is the source code for an example of such a task.
Notice that in addition to passing a reference to the scheduled executor service, we also pass an Instant representing our target moment when we want to stop our repeating task. And we pass a Duration object for the amount of time to wait between executions of this task.
Examine that run method. The task compares the current moment against the target moment. If not yet passed, the task schedules it own next execution. If passed, no rescheduling, mission accomplished.
package work.basil.example.concurrency;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ExpiringTask implements Runnable
{
private final Instant timeLimit;
private final Duration untilNextRun;
private final ScheduledExecutorService scheduledExecutorService;
public ExpiringTask ( final Instant timeLimit , final Duration untilNextRun , final ScheduledExecutorService scheduledExecutorService )
{
this.timeLimit = Objects.requireNonNull( timeLimit );
this.untilNextRun = Objects.requireNonNull( untilNextRun );
this.scheduledExecutorService = Objects.requireNonNull( scheduledExecutorService );
}
#Override
public void run ( )
{
System.out.println( "INFO - Running the `run` method of our ExpiringTask. Now: " + Instant.now() );
if ( Instant.now().isAfter( this.timeLimit ) )
{
System.out.println( "☑️ Mission accomplished. Our task has expired. Now: " + Instant.now() );
}
else
{
System.out.println( "⏲️ Not yet expired. Reschedule to check again. Now: " + Instant.now() );
this.scheduledExecutorService.schedule( this , this.untilNextRun.toNanos() , TimeUnit.NANOSECONDS );
}
}
}
In real work, when rescheduling the task, I would check that the executor service is still available. To do so, make a call to ExecutorSerivce#isShutdown. This step is omitted in code above for brevity and simplicity.
How to run get that task running? Here is the main chunk of an app to demonstrate this task running repeatedly while rescheduling itself.
First we establish our target time limit as five seconds from now. Then we specify waiting between runs for one second each time. And we get a scheduled executor service. Be sure to keep that reference around, as you must be careful to gracefully shut down the executor service eventually — otherwise its backing thread pool may continue running indefinitely, like a zombie 🧟♂️.
Next step is to instantiate our task, passing the pieces to its constructor. Finally, to get things working, we tell the scheduled executor service to run our task after a certain amount of delay. For this first run we want to run immediately, so we specify a delay of zero nanoseconds.
Instant timeLimit = Instant.now().plus( Duration.ofSeconds( 5 ) );
Duration untilNextRun = Duration.ofSeconds( 1 );
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ExpiringTask task = new ExpiringTask( timeLimit , untilNextRun , ses );
ses.schedule( task , 0 , TimeUnit.NANOSECONDS );
That covers the main idea. Here is the complete app class containing that code excerpted directly above.
package work.basil.example.concurrency;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class KeepRunningUntil
{
public static void main ( String[] args )
{
KeepRunningUntil app = new KeepRunningUntil();
app.demo();
System.out.println( "INFO - Demo ending. Now: " + Instant.now() );
}
private void demo ( )
{
System.out.println( "INFO - 'demo' method starting. " + Instant.now() );
Instant timeLimit = Instant.now().plus( Duration.ofSeconds( 5 ) );
Duration untilNextRun = Duration.ofSeconds( 1 );
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ExpiringTask task = new ExpiringTask( timeLimit , untilNextRun , ses );
ses.schedule( task , 0 , TimeUnit.NANOSECONDS );
// Wait a while for our background task to do its thing.
try { Thread.sleep( Duration.ofSeconds( 10 ).toMillis() ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); }
this.shutdownAndAwaitTermination( ses );
System.out.println( "INFO - 'demo' method ending. " + Instant.now() );
}
private void shutdownAndAwaitTermination ( ExecutorService executorService )
{
// Boilerplate taken from Javadoc.
// https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html#isShutdown()
executorService.shutdown(); // Disable new tasks from being submitted
try
{
// Wait a while for existing tasks to terminate
if ( ! executorService.awaitTermination( 60 , TimeUnit.SECONDS ) )
{
executorService.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if ( ! executorService.awaitTermination( 60 , TimeUnit.SECONDS ) )
{ System.err.println( "Pool did not terminate" ); }
}
}
catch ( InterruptedException ex )
{
// (Re-)Cancel if current thread also interrupted
executorService.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
}
When run.
INFO - 'demo' method starting. 2022-08-25T08:16:28.384638Z
INFO - Running the `run` method of our ExpiringTask. Now: 2022-08-25T08:16:28.402471Z
⏲️ Not yet expired. Reschedule to check again. Now: 2022-08-25T08:16:28.402657Z
INFO - Running the `run` method of our ExpiringTask. Now: 2022-08-25T08:16:29.408010Z
⏲️ Not yet expired. Reschedule to check again. Now: 2022-08-25T08:16:29.408126Z
INFO - Running the `run` method of our ExpiringTask. Now: 2022-08-25T08:16:30.413336Z
⏲️ Not yet expired. Reschedule to check again. Now: 2022-08-25T08:16:30.413599Z
INFO - Running the `run` method of our ExpiringTask. Now: 2022-08-25T08:16:31.416144Z
⏲️ Not yet expired. Reschedule to check again. Now: 2022-08-25T08:16:31.416230Z
INFO - Running the `run` method of our ExpiringTask. Now: 2022-08-25T08:16:32.421484Z
⏲️ Not yet expired. Reschedule to check again. Now: 2022-08-25T08:16:32.421873Z
INFO - Running the `run` method of our ExpiringTask. Now: 2022-08-25T08:16:33.425519Z
☑️ Mission accomplished. Our task has expired. Now: 2022-08-25T08:16:33.425863Z
INFO - 'demo' method ending. 2022-08-25T08:16:38.407566Z
INFO - Demo ending. Now: 2022-08-25T08:16:38.407755Z
The other answers mentioned here are good. Just giving another idea that might be simpler.
Schedule your task to run periodically and don't worry about termination
Create a class Lets call it TaskManager and make one of its members an ExecutorService. Pass the ExecutorService that you use for your task sceduling to your TaskManager. Also in your task manager add a counter that your task will increment each time that it runs. Obviously your TaskManager should have a static method incrementCounter() that your task will use. Also your TaskManager should have a method static method teminateTask().
So now when the counter reaches your limit TaskManager will call method shutDown() of your ExecutorService. Or if your task in your internal logic comes to conclusion that its time to terminate it will call method teminateTask() of your TaskManager
I want my thread to perform an action 4 or 16 times per bpm (beats per minute). This timer is not 100% accurate, after a few seconds it desynchronizes from the metronome I found on youtube, and it gets a little slower than it should.
bpm = (float) my music bpm;
thread = new Thread(()->{
long now ;
long total ;
while (true) {
long bpmTime = (long) ((1000000000*60d)/(bpm*16));
now = System.nanoTime();
bpmTick(); //<-- my task
try {
total = System.nanoTime() - now;
if(total > bpmTime) {
continue;
}
Thread.sleep((bpmTime - (System.nanoTime() - now)) / 1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
I also tried:
Thread.sleep((long)(1000*(60.0/bpm)));
but the same problem occurs
In short, I need a metronome.
Thanks in advance.
What you want to use is a ScheduledExecutorService instance instead. This class can repeat the task in a Thread repeatedly at a fixed rate. The class has a method called scheduleAtFixedRate(task, intialDelay, DelayBetweenExecutions,TimeUnit) just for this purpose.
When the initial delay is set to 0 the task will run as soon as scheduleAtFixedRate is called and will continue doing so until you call shutdown() or shutdownNow() on the service. Here's an example that uses some of your code:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
int timesPerBPM = 16;
long bpmTime = 900; //or whatever the outcome of your formula is.
Thread thread = new Thread(()->{
for (int i = 0; i < timesPerBPM; i++) //do task 16 times
bpmTick(); //<-- my task
});
executorService.scheduleAtFixedRate(thread, 0, bpmTime, TimeUnit.MILLISECONDS); //This will start immediately because initialDelay is 0.
executorService.shutdown(); //Always shutdown service when done to avoid memory leaks.
The executorService in the below code will run bpmTick() 16 times every 900 milliseconds. If it takes longer then 900 milliseconds to run bpmTick() 16 times the service will wait for the current task to complete until it starts the next repetition. So to keep things synchronized the 16 bpmTick() must always finish within bpmTime milliseconds. If you want to guarantee a 150 millisecond pause between threadtask executions regardless of how long it takes to execute a task then you should use the method scheduleWithFixedDelay() instead.
If you want the service to repeat the task every minute instead of every 900 milliseconds then it is best to change bpmTime to 1 and TimeUnit.MILLISECONDS to TimeUnit.MINUTES.
I have an application which relies on Timer and scheduling, while its main method does nothing after performing the application setup.
I see this idiom used a lot:
public static void main(final String[] args) {
// schedule all tasks
while (true) {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (final InterruptedException ignored) {
Thread.interrupted();
}
}
}
Is there a better way to write this? Do I really need this while loop for some reason? (It seems to have something to do with Thread.interrupted())
tl;dr
Do not manage threads yourself.
Use the Executors framework to create a pair of executor services.
One executor service can perform any requested tasks.
Another scheduled executor service can repeatedly check for incoming requests, indefinitely, resting for a specified amount of time between checks to free up CPU core. When a request is found, this executor service schedules a Runnable or Callable to be executed on the other executor service.
Executors framework
Is there a better way to put it?
Yes, there is a better way.
Use the Executors framework. See tutorial by Oracle.
This framework was invented so we would not have to manage threads directly. Most situations can be handled the Executors framework, with only rare cases requiring a programmer to juggle the threads.
And the Executors framework supplants the Timer & TimerTask classes. As noted in the Timer class Javadoc:
Java 5.0 introduced the java.util.concurrent package and one of the concurrency utilities therein is the ScheduledThreadPoolExecutor which is a thread pool for repeatedly executing tasks at a given rate or delay. It is effectively a more versatile replacement for the Timer/TimerTask combination, as it allows multiple service threads, accepts various time units, and doesn't require subclassing TimerTask (just implement Runnable). Configuring ScheduledThreadPoolExecutor with one thread makes it equivalent to Timer.
I assume that what you are trying to do is occasionally find a task needs to be done, and then perform that task on a background thread.
For that we need two executor services, each backed by a thread pool.
We need on executor service to perform the tasks which we discover over time need to be done. For this we use an unbounded thread pool via Executors.newCachedThreadPool. We call this our workers executor service. If you have CPU-intensive tasks, you may want an alternative executor service backed by a thread pool configured to a maximum number of threads.
We need a scheduled executor service that can be told to repeat one or more tasks every so often, on and on indefinitely. We have one task to assign here, the task that looks up what incoming requests have for other tasks to be scheduled on the other executor service. You do not explain in your Question, but I imagine you are checking a queue, or a database, or an email, or the presence of a file, for a list of new jobs to be run. That checking work is what you do here as a repeating task on this scheduled executor service. This executor service needs only a single thread in its pool, and is named dispatcher for lack of a better name.
Here is a version of the code simulating where we happen to find only one task needing to be done every time we check. We use a random number to simulate some arbitrary task being requested. To keep the code neat and tidy, we use the new switch expressions feature in Java 14. Each case in the switch generates a Runnable object, and submits that runnable object to the executor service. The executor service acts immediately to execute the run method on each runnable as it arrives upon submission. The part of the code generating the anonymous Runnable object is this lambda syntax:
( ) -> System.out.println( "Running Fruit report. Now: " + Instant.now() )
You could just as well use conventional syntax for generating the Runnable. You may well want to define separate classes that define Runnable in which you place your task code.
public class FruitReport implements Runnable
{
public void run() {
System.out.println( "Running Fruit report. Now: " + Instant.now() ) ;
}
}
Full example code.
package work.basil.example;
import java.time.Instant;
import java.util.concurrent.*;
public class TimerTaskManager
{
ExecutorService workers;
ScheduledExecutorService dispatcher;
private void launch ( )
{
System.out.println( "INFO - Method `launch` running at " + Instant.now() );
this.workers = Executors.newCachedThreadPool();
this.dispatcher = Executors.newSingleThreadScheduledExecutor();
this.dispatcher.scheduleWithFixedDelay(
( ) -> {
// Check for whatever input you have that prompts for tasks to be performed.
// We use a random number generator to simulate arbitrary work requests arriving.
// Using the new switch expressions feature in Java 14. https://openjdk.java.net/jeps/361
int r = ThreadLocalRandom.current().nextInt( 1 , 6 ); // inclusive-to-exclusive.
switch ( r )
{
case 1 -> this.workers.submit( ( ) -> System.out.println( "Running Fruit report. Now: " + Instant.now() ) ); // Passing an anonymous `Runnable` object to the `ExecutorService::submit` method.
case 2 -> this.workers.submit( ( ) -> System.out.println( "Running Wine report. Now: " + Instant.now() ) );
case 3 -> this.workers.submit( ( ) -> System.out.println( "Running Clothing report. Now: " + Instant.now() ) );
case 4 -> this.workers.submit( ( ) -> System.out.println( "Running Appliance report. Now: " + Instant.now() ) );
case 5 -> this.workers.submit( ( ) -> System.out.println( "Running Tools report. Now: " + Instant.now() ) );
default -> System.out.println( "ERROR - Unexpected r value: " + r );
}
} ,
3 ,
10 ,
TimeUnit.SECONDS
);
}
private void shutdown ( )
{
this.dispatcher.shutdown();
this.workers.shutdown();
System.out.println( "INFO - Method `shutdown` running at " + Instant.now() );
}
public static void main ( String[] args )
{
TimerTaskManager app = new TimerTaskManager();
app.launch();
try
{
Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
finally
{
app.shutdown();
}
}
}
When run.
INFO - Method `launch` running at 2020-06-28T04:16:17.742443Z
Running Wine report. Now: 2020-06-28T04:16:20.786653Z
Running Tools report. Now: 2020-06-28T04:16:30.787891Z
Running Appliance report. Now: 2020-06-28T04:16:40.791585Z
Running Wine report. Now: 2020-06-28T04:16:50.796355Z
Running Fruit report. Now: 2020-06-28T04:17:00.800407Z
Running Appliance report. Now: 2020-06-28T04:17:10.805166Z
INFO - Method `shutdown` running at 2020-06-28T04:17:17.783938Z
Let's complicate that a bit, to better simulate your likely real-world case where some arbitrary number of requests are found to be pending every time your scheduled executor service checks. To simulate this, we randomly repeat the switch statement.
package work.basil.example;
import java.time.Instant;
import java.util.concurrent.*;
public class TimerTaskManager
{
ExecutorService workers;
ScheduledExecutorService dispatcher;
private void launch ( )
{
System.out.println( "INFO - Method `launch` running at " + Instant.now() );
this.workers = Executors.newCachedThreadPool();
this.dispatcher = Executors.newSingleThreadScheduledExecutor();
this.dispatcher.scheduleWithFixedDelay(
( ) -> {
// Check for whatever input you have that prompts for tasks to be performed.
int countRequests = ThreadLocalRandom.current().nextInt( 1 , 7 );
System.out.println( "INFO - Found " + countRequests + " incoming requests for work to be done. Now: " + Instant.now() );
for ( int i = 1 ; i <= countRequests ; i++ )
{
// We use a random number generator to simulate arbitrary work requests arriving.
// Using the new switch expressions feature in Java 14. https://openjdk.java.net/jeps/361
int r = ThreadLocalRandom.current().nextInt( 1 , 6 ); // inclusive-to-exclusive.
switch ( r )
{
case 1 -> this.workers.submit( ( ) -> System.out.println( "Running Fruit report. Now: " + Instant.now() ) ); // Passing an anonymous `Runnable` object to the `ExecutorService::submit` method.
case 2 -> this.workers.submit( ( ) -> System.out.println( "Running Wine report. Now: " + Instant.now() ) );
case 3 -> this.workers.submit( ( ) -> System.out.println( "Running Clothing report. Now: " + Instant.now() ) );
case 4 -> this.workers.submit( ( ) -> System.out.println( "Running Appliance report. Now: " + Instant.now() ) );
case 5 -> this.workers.submit( ( ) -> System.out.println( "Running Tools report. Now: " + Instant.now() ) );
default -> System.out.println( "ERROR - Unexpected r value: " + r );
}
}
} ,
3 ,
10 ,
TimeUnit.SECONDS
);
}
private void shutdown ( )
{
this.dispatcher.shutdown();
this.workers.shutdown();
System.out.println( "INFO - Method `shutdown` running at " + Instant.now() );
}
public static void main ( String[] args )
{
TimerTaskManager app = new TimerTaskManager();
app.launch();
try
{
Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
finally
{
app.shutdown();
}
}
}
When run.
INFO - Method `launch` running at 2020-06-28T04:34:52.097616Z
INFO - Found 2 incoming requests for work to be done. Now: 2020-06-28T04:34:55.112823Z
Running Tools report. Now: 2020-06-28T04:34:55.122258Z
Running Appliance report. Now: 2020-06-28T04:34:55.122653Z
INFO - Found 2 incoming requests for work to be done. Now: 2020-06-28T04:35:05.127456Z
Running Appliance report. Now: 2020-06-28T04:35:05.128309Z
Running Clothing report. Now: 2020-06-28T04:35:05.128297Z
INFO - Found 5 incoming requests for work to be done. Now: 2020-06-28T04:35:15.128481Z
Running Tools report. Now: 2020-06-28T04:35:15.129414Z
Running Wine report. Now: 2020-06-28T04:35:15.129430Z
Running Appliance report. Now: 2020-06-28T04:35:15.129663Z
Running Tools report. Now: 2020-06-28T04:35:15.130001Z
Running Fruit report. Now: 2020-06-28T04:35:15.130441Z
INFO - Found 4 incoming requests for work to be done. Now: 2020-06-28T04:35:25.133727Z
Running Clothing report. Now: 2020-06-28T04:35:25.133880Z
Running Wine report. Now: 2020-06-28T04:35:25.133917Z
Running Wine report. Now: 2020-06-28T04:35:25.133967Z
Running Wine report. Now: 2020-06-28T04:35:25.134148Z
INFO - Found 6 incoming requests for work to be done. Now: 2020-06-28T04:35:35.136503Z
Running Tools report. Now: 2020-06-28T04:35:35.136663Z
Running Wine report. Now: 2020-06-28T04:35:35.136733Z
Running Clothing report. Now: 2020-06-28T04:35:35.136764Z
Running Clothing report. Now: 2020-06-28T04:35:35.136735Z
Running Appliance report. Now: 2020-06-28T04:35:35.137363Z
Running Clothing report. Now: 2020-06-28T04:35:35.137349Z
INFO - Found 3 incoming requests for work to be done. Now: 2020-06-28T04:35:45.136728Z
Running Appliance report. Now: 2020-06-28T04:35:45.136943Z
Running Clothing report. Now: 2020-06-28T04:35:45.136940Z
Running Tools report. Now: 2020-06-28T04:35:45.136948Z
INFO - Method `shutdown` running at 2020-06-28T04:35:52.111007Z
Caution: Be sure to gracefully shutdown each of your executor services when no longer needed or when your app is exiting. Otherwise their backing thread pool may continue to live indefinitely, zombie-like.
Caution: Wrap your code submitted to a scheduled executor service in a try-catch for any unexpected exceptions and possibly errors. If a throwable bubbles up to reach the scheduled executor service, that service silently stops scheduling any more work. Search Stack Overflow to learn more.
Thread.currentThread().join();
Will sleep until the JVM is killed.
See Use of Thread.currentThread().join() in Java for an explanation.
Which Java synchronisation object should I use to ensure an arbitrarily large number of tasks are completed? The constraints are that:
Each task takes a non-trivial amount of time to complete and it is appropriate to perform tasks in parallel.
There are too many tasks to fit into memory (i.e. I cannot put a Future for every task into a Collection and then call get on all the futures).
I do not know how many tasks there will be (i.e. I cannot use a CountDownLatch).
The ExecutorService may be shared so I cannot use awaitTermination( long, TimeUnit )
For example, with Grand Central Dispatch, I might do something like this:
let workQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 )
let latch = dispatch_group_create()
let startTime = NSDate()
var itemsProcessed = 0
let countUpdateQueue = dispatch_queue_create( "countUpdateQueue", DISPATCH_QUEUE_SERIAL )
for item in fetchItems() // generator returns too many items to store in memory
{
dispatch_group_enter( latch )
dispatch_async( workQueue )
{
self.processItem( item ) // method takes a non-trivial amount of time to run
dispatch_async( countUpdateQueue )
{
itemsProcessed++
}
dispatch_group_leave( latch )
}
}
dispatch_group_wait( latch, DISPATCH_TIME_FOREVER )
let endTime = NSDate()
let totalTime = endTime.timeIntervalSinceDate( startTime )
print( "Processed \(itemsProcessed) items in \(totalTime) seconds." )
It produces output that looks like this (for 128 items): Processed 128 items in 1.846794962883 seconds.
I tried something similar with a Phaser:
final Executor executor = new ThreadPoolExecutor( 64, 64, 1l, MINUTES, new LinkedBlockingQueue<Runnable>( 8 ), new CallerRunsPolicy() );
final Phaser latch = new Phaser( 0 );
final long startTime = currentTimeMillis();
final AtomicInteger itemsProcessed = new AtomicInteger( 0 );
for( final String item : fetchItems() ) // iterator returns too many items to store in memory
{
latch.register();
final Runnable task = new Runnable() {
public void run() {
processItem( item ); // method takes a non-trivial amount of time to run
itemsProcessed.incrementAndGet();
latch.arrive();
}
};
executor.execute( task );
}
latch.awaitAdvance( 0 );
final long endTime = currentTimeMillis();
out.println( "Processed " + itemsProcessed.get() + " items in " + ( endTime - startTime ) / 1000.0 + " seconds." );
The tasks do not always complete before the last print statement and I might get output that looks like this (for 128 items): Processed 121 items in 5.296 seconds. Is the Phaser even the right object to use? The documentation indicates it only supports 65,535 parties so I would need to either batch the items to be processed or introduce some sort of Phaser tiering.
The problem with the Phaser usage in this example is that the CallerRunsPolicy allows a task to execute on the initiating thread. Thus, while the loop is still in progress, the number of arrived parties can equal the number of registered parties, causing the phase to increment. The solution is to initialise the Phaser with 1 party then, when the loop is finished, arrive and wait for the other parties to arrive. This ensures the phase does not increment to 1 until all the tasks are complete.
final Executor executor = new ThreadPoolExecutor( 64, 64, 1l, MINUTES, new LinkedBlockingQueue<Runnable>( 8 ), new CallerRunsPolicy() );
final Phaser latch = new Phaser( 1 );
final long startTime = currentTimeMillis();
final AtomicInteger itemsProcessed = new AtomicInteger( 0 );
for( final String item : fetchItems() ) // iterator returns too many items to store in memory
{
latch.register();
final Runnable task = new Runnable() {
public void run() {
processItem( item ); // method takes a non-trivial amount of time to run
itemsProcessed.incrementAndGet();
final int arrivalPhase = latch.arrive();
}
};
executor.execute( task );
}
latch.arriveAndAwaitAdvance();
final long endTime = currentTimeMillis();
out.println( "Processed " + itemsProcessed.get() + " items in " + ( endTime - startTime ) / 1000.0 + " seconds." );
"to ensure an arbitrarily large number of tasks are completed" - the simplest way is to maintain a counter of completed tasks, with blocking operation to wait that given number of task is reached. There is no such ready class, but it is easy to make one:
class EventCounter {
long counter=0;
synchronized void up () {
counter++;
notifyAll();
}
synchronized void ensure (long count) {
while (counter<count) wait();
}
}
"There are too many tasks to fit into memory" - so the process of submitting new tasks must be suspended when the number of running tasks is too high. The simplest way is to consider the number of running tasks as a resource and count it with a semaphore:
Semaphore runningTasksSema=new Semaphore(maxNumberOfRunningTasks);
EventCounter eventCounter =new EventCounter ();
for( final String item : fetchItems() ) {
final Runnable task = new Runnable() {
public void run() {
processItem( item );
runningTasksSema.release();
eventCounter.up();
}
};
runningTasksSema.aquire();
executor.execute(task);
}
When a thread wants to ensure some given number of tasks are completed, it invokes:
eventCounter.ensure(givenNumberOfFinishedTasks);
Asynchronous (nonblocking) versions of runningTasksSema.aquire() and eventCounter.ensure() operations can be designed, but they would be more complex.
In case if you're on java8 you can use CompletableFuture
java.util.concurrent.CompletableFuture.allOf(CompletableFuture<?>... cfs)
that will wait for results of all futures in passed array.