Is it possible to schedule Spring service method only once at exactly specified time? For example, current time is 2pm but when I hit the action button I want that my service method starts at 8pm. I'm familiar with @Scheduled annotation and I'm not sure how to write cron expression not to run periodically. This one @Scheduled(cron = "0 0 20 * * ?")
fires every day at 8pm.
Any suggestions?

- 1,068
- 1
- 14
- 30
-
2u can use quartz scheduler :) – George Rosario May 20 '15 at 10:43
6 Answers
Dead man's solution:
@Scheduled(initialDelay = 1000 * 30, fixedDelay=Long.MAX_VALUE)
You will be dead before it fires again.

- 67,715
- 15
- 98
- 113

- 1,427
- 2
- 7
- 4
-
23
-
15What if you restart the application, code will run again :P – Bilal Ahmed Yaseen Aug 22 '20 at 14:46
-
2The Earth will also have been consumed by the sun before it fires again. – Locutus Jul 09 '22 at 22:56
-
with newest spring version i get "java.lang.ArithmeticException: long overflow" since it get converted to nanos – wutzebaer Jun 23 '23 at 12:58
You can use one of Spring's TaskScheduler's implementations. I provided an example below with one which does not require too much configuration (ConcurrentTaskScheduler that wraps a single-threaded scheduled executor).
The simplest method is the one named schedule that takes a Runnable and Date only. That will cause the task to run once after the specified time. All of the other methods are capable of scheduling tasks to run repeatedly.
Read more on task execution & scheduling
Simple working example:
private TaskScheduler scheduler;
Runnable exampleRunnable = new Runnable(){
@Override
public void run() {
System.out.println("Works");
}
};
@Async
public void executeTaskT() {
ScheduledExecutorService localExecutor = Executors.newSingleThreadScheduledExecutor();
scheduler = new ConcurrentTaskScheduler(localExecutor);
scheduler.schedule(exampleRunnable,
new Date(1432152000000L));//today at 8 pm UTC - replace it with any timestamp in miliseconds to text
}
...
executeTaskT() //call it somewhere after the spring application has been configured
Note:
To enable support for @Scheduled and @Async annotations add @EnableScheduling and @EnableAsync to one of your @Configuration classes
Update - cancelling the scheduled task
TaskScheduler's schedule method returns a ScheduledFuture which is a delayed result-bearing action that can be cancelled.
So in order to cancel it, you need to keep a handle to the scheduled task (i.e. keep the ScheduledFuture return object).
Changes to the code above for cancelling the task :
- Declare the ScheduledFuture outside your executeTaskT method.
private ScheduledFuture scheduledFuture;
- Modify your call to schedule to keep the return object as such:
scheduledFuture = scheduler.schedule(exampleRunnable,
new Date(1432152000000L));
- Call cancel on the scheduledFuture object somewhere in your code
boolean mayInterruptIfRunning = true;
scheduledFuture.cancel(mayInterruptIfRunning);

- 5,344
- 2
- 22
- 27

- 6,566
- 1
- 34
- 60
-
This works. I expanded ConcurrentTaskScheduler instantiation with server thread management task executor like this `scheduler = new ConcurrentTaskScheduler( workManagerTaskExecutor, Executors.newSingleThreadScheduledExecutor() )`. It is a type of `org.springframework.scheduling.commonj.WorkManagerTaskExecutor` and is provided by server JNDI register. – shx May 21 '15 at 04:30
-
1Great, worked for me. btw, if runnable with parameters is needed, you can write a method which is accepting parameter and returns Runnable and then pass this method in scheduler with parameter scheduler.schedule(createRunnable(param1), new Date(1432152000000L)); – J-Alex Nov 18 '16 at 08:18
-
1
-
Is there a reason, why you also annotated `executeTaskT` with `@Async`? – manuelwaldner Sep 15 '21 at 05:16
-
@manuelwaldner so that Spring will execute it in a separate thread and the caller of the method will not wait till the method is completed execution – Laurentiu L. Sep 20 '21 at 13:42
-
I know, i meant, why he is doing it here, as the method is only scheduling another task, which is also just putting the task to being executed on another thread. In this case it's doing it like this: Thread (which is calling `executeTaskT`) -> Thread (executes `executeTaskT`) -> Thread (executes `exampleRunnable`) – manuelwaldner Sep 22 '21 at 10:18
-
I thought that the first answer is not suitable because with each restart of application the job will trigger, but with this solution it is happening too I just want to run it one time, And keep it as documented code in project how is it possible? – Sobhan Jan 02 '22 at 08:21
You can extend PeriodicTrigger as follows - it checks the lastCompletionTime: it will be null if the task has never run before. You can try variation of this if you want to run the task just once at some given time.
class RunOnceTrigger extends PeriodicTrigger {
public RunOnceTrigger(long period) {
super(period);
setInitialDelay(period);
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
if(triggerContext.lastCompletionTime() == null) { // hasn't executed yet
return super.nextExecutionTime(triggerContext);
}
return null;
}
}

- 3,122
- 29
- 31
In order to not create ScheduledExecutorService
and ConcurrentTaskScheduler
at every method call it is convenient to initialize TaskScheduler
at service creation, e.g.
private final TaskScheduler taskScheduler =
new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(10));
@Async
has no sense as we just schedule task and exit from method.

- 16,647
- 10
- 125
- 197
Since Spring 6.0.10/Spring Boot 3.0.8 the implementation of ThreadPoolTaskScheduler converts all delays to nanoseconds. Unfortunatly the annotation @Scheduled
uses the TimeUnit
milliseconds, doing
@Scheduled(initialDelay = ..., fixedDelay=Long.MAX_VALUE)
will break the application with an java.lang.ArithmeticException: long overflow
because converting Long.MAX_VALUE
milliseconds will not fit in a long.
An easy workorund is to limit the fixed delay by choosing nanoseconds
@Scheduled(initialDelay = 1000 * 30, fixedDelay=Long.MAX_VALUE, timeUnit = TimeUnit.NANOSECONDS )
This will reduce the delay from 300 million years to less than 300 years, but this should suffice for even the most pessemistic deployment schedule.
Note that there is a fix already on the way: https://github.com/spring-projects/spring-framework/commit/599ac58baa049d0075edf802cf056ffa7112fc87

- 33,984
- 10
- 106
- 126
The title of the question implies a one-shot task runner is required. Here is a task class that will run once after a delay (or replace with an epoch as the OP requests). Additionally, you can enable/disable it in the application.properties/yaml or with profiles.
@Slf4j
@Component
@ConditionalOnProperty({ "app.scheduler.onceRunner" })
public class RunOnceTask
{
public static final long INITIAL_DELAY_SECS = 5L; // As you wish
@Autowired
@Qualifier("mainTaskScheduler") // You could set a custom TPTS to set threads, name, etc.
private ThreadPoolTaskScheduler poolTaskScheduler;
@PostConstruct
public void run()
{
poolTaskScheduler.scheduleWithFixedDelay( () -> {
log.info( "Once-runner ran once" );
}, new Date( System.currentTimeMillis() + (INITIAL_DELAY_SECS * 1000) ), Long.MAX_VALUE ); // Run once
}
}

- 23,254
- 3
- 51
- 94