停止Spring计划执行,如果它在一段固定时间后挂起

我使用Spring Framework的Scheduled来安排我的工作使用cron每5分钟运行一次。 但有时我的工作无限期地等待外部资源,我不能把超时放在那里。 我不能使用fixedDelay作为先前的进程有时进入等待无限模式,我必须每隔5分钟刷新一次数据。

因此,我在Spring Framework的Scheduled寻找任何选项,在fixed-time之后停止该进程/线程,无论它是否成功运行。

我发现下面的设置初始化ThreadPoolExecutor为120秒, keepAliveTime放在@Configuration类中。 任何人都能告诉我这项工作是否符合我的预期。

 @Bean(destroyMethod="shutdown") public Executor taskExecutor() { int coreThreads = 8; int maxThreads = 20; final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( coreThreads, maxThreads, 120L, TimeUnit.SECONDS, new LinkedBlockingQueue() ); threadPoolExecutor.allowCoreThreadTimeOut(true); return threadPoolExecutor; } 

我不确定这会按预期工作。 确实keepAlive用于IDLE线程,我不知道你的线程在等待资源是否处于IDLE状态。 此外,只有当线程数大于核心时,除非您监视线程池,否则您无法确切知道它何时发生。

keepAliveTime – 当线程数大于核心时,这是多余空闲线程在终止之前等待新任务的最长时间。

你能做的是以下几点:

 public class MyTask { private final long timeout; public MyTask(long timeout) { this.timeout = timeout; } @Scheduled(cron = "") public void cronTask() { Future result = doSomething(); result.get(timeout, TimeUnit.MILLISECONDS); } @Async Future doSomething() { //what i should do //get ressources etc... } } 

不要忘记添加@EnableAsync

通过实现Callable,也可以在没有@Async的情况下执行相同的操作。

编辑:请记住,它将等到超时,但运行任务的线程不会被中断。 发生TimeoutException时,您需要调用Future.cancel。 并在任务检查isInterrupted()以停止处理。 如果您正在调用api,请确保选中isInterrupted()。

allowCoreThreadTimeOuttimeout设置没有帮助导致它只允许工作线程在一段时间后无法工作(参见javadocs)

你说你的工作无限期地等待外部资源。 我确定这是因为您(或您使用的某些第三方库)使用的套接字默认超时。 还要记住,当它在socket.connect / read上被阻塞时,jvm会忽略Thread.interrupt()。

因此,找出您的任务中使用的女巫套接字库(以及它是如何使用的)并更改它的默认超时设置。

例如: RestTemplate在Spring中广泛使用(在rest客户端,春季社交,春季安全OAuth等)。 并且有ClientHttpRequestFactory实现来创建RestTemplate实例。 默认情况下,spring使用SimpleClientHttpRequestFactory ,它使用JDK套接字。 默认情况下,所有超时都是无限的。

因此,找出你准确冻结的位置,阅读它的文档并正确配置它。

PS如果你没有足够的时间和“感觉幸运”尝试运行你的应用程序将jvm属性sun.net.client.defaultConnectTimeoutsun.net.client.defaultReadTimeout设置为一些合理的值(有关更多详细信息,请参阅文档 )

keepAliveTime仅用于清除一段时间内不需要的工作线程 – 它对提交给执行程序的任务的执行时间没有任何影响。

如果花费时间考虑中断,您可以启动一个新线程并加入超时,如果没有及时完成则中断它。

 public class SomeService { @Scheduled(fixedRate = 5 * 60 * 1000) public void doSomething() throws InterruptedException { Thread taskThread = new TaskThread(); taskThread.start(); taskThread.join(120 * 000); if(taskThread.isAlive()) { // We timed out taskThread.interrupt(); } } private class TaskThread extends Thread { public void run() { // Do the actual work here } } }