I am playing with ScheduledExecutorService. What I want to do is to start a simple ticker (one tick per second) and schedule another task later (after five seconds) which cancels the first one. And then block the main thread until everything finishes, which should be after both tasks finish (+- five seconds).
This is my code:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Runnable tickTask = () -> System.out.println("Tick");
ScheduledFuture<?> scheduledTickTask = executor.scheduleAtFixedRate(tickTask, 0, 1, TimeUnit.SECONDS);
Runnable cancelTask = () -> scheduledTickTask.cancel(true);
executor.schedule(cancelTask, 5, TimeUnit.SECONDS);
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
The problem which suprises me is that it BLOCKS as if there were still some running tasks. Why? The cancelTask should end immediately and the scheduledTickTask was just cancelled, so what is the problem?
As per the Javadoc of ExecutorService.awaitTermination (emphasis mine):
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
That means you need to call shutdown first, like this:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Runnable tickTask = () -> System.out.println("Tick");
ScheduledFuture<?> scheduledTickTask = executor.scheduleAtFixedRate(tickTask, 0, 1, TimeUnit.SECONDS);
Runnable cancelTask = () -> {
scheduledTickTask.cancel(true);
executor.shutdown();
};
executor.schedule(cancelTask, 5, TimeUnit.SECONDS);
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
In your case, the timeout will never happen because you practically set it to "infinity" and the current thread is not interrupted.
Related
I have a few tasks which are registered by
final ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
List<ScheduledFuture<?>> futures = new ArrayList<>();
tasks.forEach(task->{
var future = ses.scheduleWithFixedDelay(() -> run(task), 0, 3, TimeUnit.SECONDS);
futures.add(future);
});
// and now cancel all tasks one for one after 10 seconds..
ses.scheduleWithFixedDelay(() ->
{
log.info("cancel task----");
futures.get(0).cancel(false);
}, 0, 10, TimeUnit.SECONDS);
As you can see, for each task the futures holds a task.getId() so I can obtain the ScheduledFuture of a task afterwards. I do not want to ses.shutdown() because this will shutdown the whole schedulings for the other tasks as well, which I want to avoid.
The only solution I actually see is to create one ScheduledExecutorService for each task to be able to shutdown it afterwards for a specified task, but then I cannot make use of the pooling.
How can I shutdown only a specified task within the pool?
Use
Future<?> future;
future.cancel(false);
Cancel will cancel the task and any further scheduling of it.¹ The Boolean parameter decides if you want to throw an interruption exception on the task if it is already running and blocking on a resource.
To ensure the task is removed from the queue immediately upon cancelling, use the setRemoveOnCancelPolicy method on your ScheduledThreadPoolExecutor and set the policy to true.²
final ScheduledThreadPoolExecutor ses = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10);
ses.setRemoveOnCancelPolicy(true);
¹ https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/Future.html
² https://stackoverflow.com/a/36748183/4425643 , https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#setRemoveOnCancelPolicy-boolean-
Is there any way to schedule CompletableFuture in Java?
What I wanted to do is to schedule a task to be executed with some delay, and chain it with other operations to be performed asynchronously when it completes. So far I didn't find any way to do this.
For good ol' Futures we have e.g. ScheduledExecutorService, where we can schedule a task to be executed with some delay like this:
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Future<String> future = scheduledExecutorService.schedule(() -> "someValue", 10, TimeUnit.SECONDS);
Is there any similar way for CompletableFutures?
If you're using Java 9+ then CompletableFuture#delayedExecutor(long,TimeUnit) may fit your needs:
Returns a new Executor that submits a task to the default executor after the given delay (or no delay if non-positive). Each delay commences upon invocation of the returned executor's execute method.
Executor delayed = CompletableFuture.delayedExecutor(10L, TimeUnit.SECONDS);
CompletableFuture.supplyAsync(() -> "someValue", delayed)
.thenAccept(System.out::println)
.join();
There's also an overload where you can specify the Executor to use in place of the "default executor".
As said, there is support in Java 9.
But it’s not hard to create a similar feature under Java 8; you already named the necessary elements:
// prefer this constructor with zero core threads for a shared pool,
// to avoid blocking JVM exit
static final ScheduledExecutorService SCHEDULER = new ScheduledThreadPoolExecutor(0);
static Executor delayedExecutor(long delay, TimeUnit unit)
{
return delayedExecutor(delay, unit, ForkJoinPool.commonPool());
}
static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
{
return r -> SCHEDULER.schedule(() -> executor.execute(r), delay, unit);
}
which can be used similarly to the Java 9 feature:
Executor afterTenSecs = delayedExecutor(10L, TimeUnit.SECONDS);
CompletableFuture<String> future
= CompletableFuture.supplyAsync(() -> "someValue", afterTenSecs);
future.thenAccept(System.out::println).join();
Care must be taken to avoid that the shared scheduled executor’s threads prevent the JVM from terminating. The alternative to a zero core pool size is to use daemon threads:
static final ScheduledExecutorService SCHEDULER
= Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
How to fix my thread to schedule initial delay of thread for 2 min and don't schedule it again. (i.e., schedule only for once)
#Scheduled(fixedDelay = 5000)
public void myJob() {
Thread.sleep(12000);
}
You can use ScheduledExecutorService in this case. It is an ExecutorService that can schedule commands to run after a given delay, or to execute periodically.
ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit)
Creates and executes a ScheduledFuture that becomes enabled after the
given delay.
callable - the function to execute
delay - the time from now to delay execution
unit - the time unit of the delay
ScheduledExecutorService service = null;
service = Executors.newScheduledThreadPool(1);
service.schedule(() -> {
myMethodNameWhichIWantToExecute();
}, 2, TimeUnit.MINUTES);
if (service != null) service.shutdown();
I have two newSingleThreadScheduledExecutor, scheduledService1 and scheduledService2.
ScheduledExecutorService scheduledService1 = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = () -> System.out.println("Hello zoo1");
Callable<String> task2 = () -> "Monkey";
ScheduledFuture<?> result1 = scheduledService1.schedule(task1, 5, TimeUnit.SECONDS);
System.out.println(result1.get());
Future<?> result2 = scheduledService1.schedule(task2, 5, TimeUnit.SECONDS);
System.out.println(result2.get());
Runnable task3 = () -> System.out.println("Hello zoo2");
ScheduledExecutorService scheduledService2 = Executors.newSingleThreadScheduledExecutor();
scheduledService2.schedule(task3, 5, TimeUnit.SECONDS);
//blocked by scheduledService1?
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(() -> System.out.println("new single thread executor"));
System.out.println("main thread");
This outputs:
Hello zoo1
null
Monkey
main thread
new single thread executor
Hello zoo2
Based on the output, it seems that scheduledService1 blocks the main thread and es thread. Why is this so? Since "Hello zoo2" is printed last (from scheduledService2 task), why does it not block main and es thread as well. These are below the scheduledService2 declaration after all. Are my assumptions correct that only the first ScheduledExecutorService will block other threads and not the succeeding ScheduledExecutorService instance?
ScheduledFuture<?> result1 = scheduledService1.schedule(task1, 5, TimeUnit.SECONDS);
Neither scheduling task1, nor its execution will block the calling thread, as scheduledService1 uses its own background thread pool.
But, calling get on a Future will block the caller until the result is ready (i.e. the scheduled task has run to completion):
System.out.println(result1.get()); // this will block for 5 seconds
I want to use method newWorkStealingPool() to get thread and run them continuously every 1 sec. Using the following sample code :
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Scheduling: " + System.currentTimeMillis());
int initialDelay = 0;
int period = 1;
executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS);
I can run task continuously but I want to use the method newWorkStealingPool() to get threads. Using the following code:
ScheduledExecutorService executor = (ScheduledExecutorService)Executors.newWorkStealingPool();
Runnable task = () -> System.out.println("Scheduling: " + System.currentTimeMillis());
int initialDelay = 0;
int period = 1;
executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS);
I got the error:
java.util.concurrent.ForkJoinPool cannot be cast to java.util.concurrent.ScheduledExecutorService
Using ExecutorService object it's possible to use newWorkStealingPool() but I don't know if is there any way to run ExecutorService object continuously like what object ScheduledExecutorService provides?
I think this can be achieved with creating ScheduledExecutorService and ForkJoinPool. ScheduledExecutorService will be used to submit tasks to ForkJoinPool at specified intervals. And ForkJoinPool will execute these tasks.
ForkJoinPool executor = (ForkJoinPool) Executors.newWorkStealingPool();
// this will be only used for submitting tasks, so one thread is enough
ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
Runnable task = () -> System.out.println("Scheduling: " + System.currentTimeMillis());
int initialDelay = 0;
int period = 1;
scheduledExecutor.scheduleAtFixedRate(()->executor.submit(task), initialDelay, period, TimeUnit.SECONDS);
The Executors.newWorkStealingPool() produces a ForkJoinPool. The ForkJoinPool class does not implement the ScheduledExecutorService interface so you cannot cast it to a ScheduledExecutorService.
Furthermore the ForkJoinPool and ScheduledExecutorService are fundamentally different thread pools. If you need to schedule a task to execute once every second stick with a ScheduledExecutorService, since it is suitable for your use case. ForkJoinPools are intended to use in cases where you have many small units of work divided among many threads, not for when you want to regularly execute something.