我在我的项目中有三个方法用@调度注释,其中一个是cron表达式,另外两个是固定延迟。注释如下所示:
方法1:
@Scheduled(fixedDelay = 20000)
@Async
protected void checkBrokenEngines() {
方法2:
@Scheduled(fixedRate = 20000)
@Async
public void checkAvailableTasks() throws Exception {
方法3:
@Scheduled(cron = "0 0 2 * * ?")
protected void deleteOldData() {
以前,我有一个问题,当check BrokenEngines
和check可用性任务
方法执行缓慢时,下一次执行直到上一次结束才发生。从StackOverflow读取留档和这里的一些主题,我看到我的项目有一些错误的池大小设置,并且这些方法没有用异步注释。(异步是为了下一次执行开始,即使旧的没有结束,因为这不会在我的应用程序中引起任何问题)
现在又出现了另一个问题,就是我的问题:
当执行deleteOldData()
方法时,其他两个方法在完成之前都不会运行。看到此方法阻止了其他两个方法的执行后,我将此方法注释为异步,然后,即使此方法需要时间才能执行,也会在规定的时间内正确调用其他两个方法。为什么?在我的理解中,这不应该发生,因为这些方法是用async标记的,并且池有足够的空间来执行它们。
PS:deleteOldData()
不能异步。它必须在前一次执行完成后立即启动。
编辑1:
异步执行器配置:
@Override
public AsyncTaskExecutor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(50);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(10000);
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
之所以会发生这种情况,是因为@异步任务是由默认的调度执行器提交的,其大小默认为1。
我修改了AsyncTaskExecutor的提交方法:
@Bean
AsyncConfigurer asyncConfigurer() {
return new AsyncConfigurer() {
@Override
public AsyncTaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(){
@Override
public <T> Future<T> submit(Callable<T> task) {
System.out.println("async task was started by thread -- "+Thread.currentThread().getName());
return super.submit(task);
}
};
executor.setThreadNamePrefix("custom-async-exec");
executor.setCorePoolSize(2);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
};
}
和输出。
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
因此,默认调度程序池中有1个线程调度-1,当其繁忙时无法启动/提交新的异步任务。定义Bean线程池TaskExecutor或添加spring。任务行程安排。水塘尺寸=x。
编辑
以下是可视化的简单测试:
@Component
public static class Jobs{
@Scheduled(fixedDelay = 1500)
@Async
public void job1(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Scheduled(fixedDelay = 1500)
@Async
public void job2(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Scheduled(initialDelay = 10000, fixedDelay = 5000)
public void blocking(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
可视化来自visualvm
红色“箭头”显示作业开始的点。虽然调度-1线程被阻止,但也无法提交job1()和job2()
计划的任务由ThreadPoolTaskScheduler
处理,它的默认池大小为1。只有当它们被注释为@Async
时,执行才会传递到AsyncTaskExecator
,它为您配置了一个具有更大池大小的专用执行程序。
要在配置类中配置ThreadPoolTaskScheduler,请执行以下操作:
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
scheduler.setPoolSize(50);
return scheduler ;
}
所有已调度的标记调用都将使用默认的单线程执行器来调度任务(异步或其他)。
所有@Async
任务都被移交给不同的aysnc线程池执行器,以便使用AOP拦截器执行。
我认为您的困惑来自异步方法立即返回的事实,但当deleteOldData运行时,它是同步运行的,因为只有一个线程,它会阻止任何其他计划任务的执行。
由于计划任务有默认的线程池(单线程),因此会一个接一个地计划这些任务。
其他带有@Async注释的方法即使完成或未完成也会执行。在某些情况下,我有两种同时执行的方法。但在执行deleteOldData时,异步方法会停止运行,直到完成为止。这是我不理解的,抱歉:/–
这与调度不同——这是您的异步执行程序发挥作用的地方,它们同时运行。
您可以通过以下两种方式之一修复此问题:
您可以在应用程序属性中使用spring.task.scheduling.pool.size=10
来设置任务调度程序的池大小。
或者,使用不同的任务调度器。继续为计划的任务使用默认计划程序,并为异步任务配置如下内容(删除计划注释)
需要增强功能才能将任务调度器传递到计划的注释,直到您必须手动计划任务。
为异步调用注册一个新的任务调度器,并在构造后阶段调度方法。类似于
配置
@Bean("asyncTaskScheduler")
public TaskScheduler asyncTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
服务
@Autowired
private TaskScheduler asyncTaskScheduler;
@PostConstruct
void schedule() {
asyncTaskScheduler.scheduleAtFixedRate(this::checkAvailableTasks, 20000L);
asyncTaskScheduler.scheduleAtFixedDelay(this::checkBrokenEngines, 20000L);
}
@Async
public void checkBrokenEngines() {...}
@Async
public void checkAvailableTasks() throws Exception {...}
对于这个程序,实例变量的创建和模型需要是一个字符串,价格需要是我已经有的两倍,但不确定如何处理需要是int类型的年份,大于1900。然后我需要用参数做一个构造函数,我也做了,但是toString需要用setter和getters方法返回Car对象的字符串表示。所以我在试图为setter想出一些东西时遇到了问题,如果我做对了这一部分。 这部分是Cartest驱动程序,我不确定我这样做是否正确。我必须
Spring使用Quartz的顺序作业计划 我有三个或更多的工作,这取决于他们各自以前的工作,他们将按顺序运行。如果完成运行,当完成运行。如果在上一个中发生任何错误,则不会激发下一个触发的作业。我试图了解工作链使用石英,但无法通过它。 作业顺序如下所示 提前谢了。
我想每天使用Spring Boot发送电子邮件,用户指定发送时间,我使用石英来安排我的工作。电子邮件的收件人有(id、emailAddress、截止日期)电子邮件将发送给截止日期=今天X...(用户指定X)。例如:用户指定X是1号,所以我们对明天有截止日期的人感兴趣。 第1天:应用程序向截止日期为今天1的人发送电子邮件。。第二天:我希望应用程序在第二天将电子邮件发送给新的收件人,但使用下面的代码,
问题内容: 我需要在Flask应用程序上定期运行某些任务。我决定使用一个简单的库-Schedule(https://github.com/dbader/schedule)来执行此操作。我在与主应用程序线程不同的线程上运行任务计划程序。这是相关的代码片段。 运行此程序时,我想要“运行定期任务!” 每10秒打印一次。但是,这是我得到的输出。 显然,由于某种原因,任务似乎每10秒执行两次,而不是一次。但
我对使用REST API的订阅功能有一些疑问。我们已经使用“快速结账NVP/SOAP集成”实现了定期支付,但对我们来说这不是最佳选择,因为: Webhooks比IPN消息更容易、更可用; 我们不能强迫顾客从PayPal余额中付款。 所以我想用REST API重写。我认为流程会是这样的: < li >用户按下按钮,我们第一次请求获取身份验证令牌; < li >创建计费计划; < li >启用计费计划
我希望每天早上9点完成一项任务。我得到了一些有趣的结果。目前我的工作是这样的: 这是怎么回事?这是正确的表达吗?