为什么ScheduledExecutorService.schedule()启动的线程永远不会终止?
当我通过调用ScheduledExecutorService.schedule()创建一个线程时,它在执行计划任务后永远不会终止。
例如,以下程序永远不会退出:
public static void main(String[] args) { ScheduledFuture scheduledFuture = Executors.newSingleThreadScheduledExecutor().schedule(new Callable() { public Void call() { doSomething(); return null; } }, 1, TimeUnit.SECONDS); } public static void doSomething() { }
这是一个JDK错误,还是我错过了什么?
计划任务正在执行或正在等待执行。
如果任务正在等待执行,则future.cancel()
将阻止它被执行(取消(true)/取消(false))。
如果任务已在执行,则future.cancel(false)
将不起作用。 future.cancel(true)
将中断正在执行该任务的线程。 这是否会产生任何影响取决于您 ,谁将执行该任务。 根据实施,任务可能会也可能不会响应中断。
为了使您的任务响应取消,您必须实现doSomething()
以便它能够响应中断。
基本上有两种方法可以做到这一点:
1.检查逻辑中的中断标志
public void doSomething(){ stuff(); //Don't use Thread.interrupt() if(Thread.currentThread().isInterrupted()){ // We have an interruption request, possibly a cancel request //Stop doing what you are doing and terminate. return; } doLongRunningStuff(); }
您必须偶尔检查中断标志,如果中断,请停止正在进行的操作并返回。 请务必使用Thread.isInterrupted()而不是Thread.interrupt()进行检查。
2.Act on Interrupted exception
public void doSomething(){ try{ stuff(); }catch(InterruptedException e){ // We have an interruption request, possibly a cancel request // First, preserve Interrupted Status because InterruptedException clears the // interrupted flag Thread.currentThread.interrupt(); // Now stop doing your task and terminate return; } doLongRunningStuff(); }
当你有任何抛出InterruptedException的方法时,一定要停止你做的事情并在抛出一个时终止。
以这种方式实现方法后,可以调用future.cancel(true)来取消正在运行的任务的执行。
您的程序永远不会终止,因为您创建了一个ScheduledExecutorService
,它包含一个线程池,但您永远不会关闭该服务。 因此,线程池中的用户线程永远不会终止,因此VM将永远运行。
要解决此问题,您需要在执行程序服务上调用shutdown()
。 这甚至可以在安排您要执行的任务后直接完成:
public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture scheduledFuture = executorService.schedule(new Callable() { public Void call() { doSomething(); return null; } }, 1, TimeUnit.SECONDS); executorService.shutdown(); }
这将正常执行计划任务,然后终止池中的线程。
您需要调用scheduledExecutorService.shutdown()
来停止执行。 否则每秒重启一次。
(已编辑:见评论)
- 如何从servlet中显示jsp中的警报,然后重定向到另一个jsp?
- 如何在JGit中从一个git分支硬重置到另一个?
- Jackson ObjectMapper DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
- 在Spring上下文中导入/过滤属性
- Shadow Plugin Gradle:mergeServiceFiles()有什么作用?
- 国家/地区代码(iso-3166-1 / iso-3166-2)到经度和纬度
- JDBC连接池:连接重用?
- 配置Log4j属性路径的最佳实践
- 使用Apache POI for Java在现有Excel工作簿中创建新工作表