Reputation: 43
I'm just exploring method scheduleAtFixedRate of class ScheduledExecutorService in Java.
Here is my suspicious code:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
Runnable command = () -> {
System.out.println("Yo");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
scheduledExecutorService.scheduleAtFixedRate(command, 0, 1, TimeUnit.SECONDS);
I expected that every 1 second scheduledExecutorService will try to take new thread from the pool and start it.
API says: "scheduledExecutorService creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period. /(unimportant deleted)/ If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute."
Result - every new thread starts every 4 seconds.
So, the questions:
What's the catch - Does Thread.sleep() stop all threads or nuance in this behavior - "If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute"?
If "will not concurrently execute" is true in this situation - why do we need this pool of several threads if every thread will start after execution of previous thread?
Is there any simple valid example of usage of scheduleAtFixedRate, where one thread starts while previous still executes?
Upvotes: 4
Views: 2759
Reputation: 95
public class Poll {
ScheduledFuture<?> future;
static int INIT_DELAY = 1;
static int REPEAT_PERIOD = 2;
static int MAX_TRIES = 3;
int tries = 1;
Runnable task = () -> {
System.out.print( tries + ": " + Thread.currentThread().getName() + " " );
if ( ++tries > MAX_TRIES ) {
future.cancel( false );
}
};
void poll() {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
future = executor.scheduleAtFixedRate( task, INIT_DELAY, REPEAT_PERIOD, TimeUnit.SECONDS );
System.out.println( "Start: " + tries + ": " + Thread.currentThread().getName() + " " );
try {
future.get();
} catch ( InterruptedException | ExecutionException e ) {
System.out.println( e.getMessage() );
} catch ( CancellationException e ) {
System.out.println( "Regular End Of Scheduled Task as Designed.");
} finally {
executor.shutdown();
executor.shutdownNow();
}
System.out.println( "Return The Result." );
}
// The Driver
public static void main( String[] args ) {
new Poll().poll();
}
}
Upvotes: 0
Reputation: 925
What's the catch - Does Thread.sleep() stop all threads or nuance in this behavior - "If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute"?
If "will not concurrently execute" is true in this situation - why do we need this pool of several threads if every thread will start after execution of previous thread?
Is there any simple valid example of usage of scheduleAtFixedRate, where one thread starts while previous still executes?
Upvotes: 1
Reputation: 3890
The answer is in the quote you provided. Executor waits until the task finishes before launching this task again. It prevents concurrent execution of many instances of one task - in most cases this behaviour is needed. In your case Executor starts a task, then waits 1 second of delay, then waits 3 more seconds until current task is done and only then starts this task again (It does not necessarily start new thread, it may start the task in the same thread).
Your code does not use thread pool at all - you can get exactly same result using single thread executor.
If you want to get this behaviour:
I expected that every 1 second scheduledExecutorService will try to take new thread from the pool and start it.
Then you may write is like this:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
Runnable command = () -> {
System.out.println("Yo");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable commandRunner = () -> {
scheduledExecutorService.schedule(command, 0, TimeUnit.SECONDS);
}
scheduledExecutorService.scheduleAtFixedRate(commandRunner, 0, 1, TimeUnit.SECONDS);
(It's better to create a single-threaded ScheduledExecutorService
that runs commandRunner
and create a thread pool based ExecutorService
that is used by commandRunner
to execute command
)
Upvotes: 1