Java Spring调度任务——只运行一次
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30347233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring scheduling task - run only once
提问by shx
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?
是否可以在指定的时间只安排一次 Spring 服务方法?例如,当前时间是下午 2 点,但是当我点击操作按钮时,我希望我的服务方法在晚上 8 点开始。我熟悉 @Scheduled 注释,我不确定如何编写不定期运行的 cron 表达式。这个@Scheduled(cron = "0 0 20 * * ?")
每天晚上8点开火。
有什么建议?
采纳答案by Laurentiu L.
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).
您可以使用 Spring 的 TaskScheduler 实现之一。我在下面提供了一个不需要太多配置的例子(ConcurrentTaskScheduler 包装了一个单线程调度执行器)。
The simplest method is the one named schedulethat takes a Runnable and Date only. That will cause the task to run once after thespecified time. All of the other methods are capable of scheduling tasks to run repeatedly.
最简单的方法是一个名为schedule 的方法,它只接受 Runnable 和 Date。这将导致任务在指定时间后运行一次。所有其他方法都能够安排任务重复运行。
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
要启用对 @Scheduled 和 @Async 注释的支持,请将 @EnableScheduling 和 @EnableAsync 添加到您的 @Configuration 类之一
Update- cancelling the scheduled task
更新- 取消计划任务
TaskScheduler's schedule method returns a ScheduledFuture which is a delayed result-bearing action that can be cancelled.
TaskScheduler 的 schedule 方法返回一个 ScheduledFuture,它是一个可以取消的延迟的结果承载操作。
So in order to cancel it, you need to keep a handle to the scheduled task (i.e. keep the ScheduledFuture return object).
所以为了取消它,你需要保留一个计划任务的句柄(即保留 ScheduledFuture 返回对象)。
Changes to the code above for cancelling the task :
更改上面的代码以取消任务:
- Declare the ScheduledFuture outside your executeTaskT method.
- 在 executeTaskT 方法之外声明 ScheduledFuture。
private ScheduledFuture scheduledFuture;
- Modify your call to schedule to keep the return object as such:
- 修改您对 schedule 的调用以保持返回对象如下:
scheduledFuture = scheduler.schedule(exampleRunnable,
new Date(1432152000000L));
- Call cancel on the scheduledFuture object somewhere in your code
- 对代码中某处的 scheduleFuture 对象调用取消
boolean mayInterruptIfRunning = true;
scheduledFuture.cancel(mayInterruptIfRunning);
回答by Grigory Kislin
In order to not create ScheduledExecutorService
and ConcurrentTaskScheduler
at every method call it is convenient to initialize TaskScheduler
at service creation, e.g.
为了不创建ScheduledExecutorService
并且ConcurrentTaskScheduler
在每次方法调用时都可以方便地TaskScheduler
在服务创建时进行初始化,例如
private final TaskScheduler taskScheduler =
new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(10));
@Async
has no sense as we just schedule task and exit from method.
@Async
没有意义,因为我们只是安排任务并退出方法。
回答by smishra
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.
您可以按如下方式扩展 PeriodicTrigger - 它检查 lastCompletionTime:如果任务以前从未运行过,它将为空。如果您想在某个给定时间只运行一次任务,您可以尝试这种变体。
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;
}
}
回答by Rick Ahlander
Dead man's solution:
死人的解决办法:
@Scheduled(initialDelay = 1000 * 30, fixedDelay=Long.MAX_VALUE)
@Scheduled(initialDelay = 1000 * 30, fixedDelay=Long.MAX_VALUE)
You will be dead before it fires again.
你会死在它再次开火之前。
回答by Drakes
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/yamlor with profiles.
问题的标题暗示需要一次性任务运行器。这是一个将在延迟后运行一次的任务类(或替换为 OP 请求的纪元)。此外,您可以在application.properties/yaml或配置文件中启用/禁用它。
@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
}
}