ScheduledExecutorService - 单次操作后程序未结束
Posted
技术标签:
【中文标题】ScheduledExecutorService - 单次操作后程序未结束【英文标题】:ScheduledExecutorService - program not ending after one-shot action 【发布时间】:2015-02-08 03:19:52 【问题描述】:我的程序中有一个计划任务,它在给定时间段后关闭一个框架。但是,在任务执行后,程序继续运行,就好像 ScheduledExecutorService
仍在另一个线程上运行一样。
这是我的代码的相关部分:
int delay = 1000;
ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
ex.schedule(() ->
System.out.println("executed");
getWindow().closeWindow();
// ex.shutdown();
, delay, TimeUnit.MILLISECONDS);
这里任务在 1 秒延迟后执行,“已执行”打印一次,框架关闭,即使在此代码之后程序仍继续运行。如果我取消注释ex.shutdownNow();
,程序会按预期成功结束。但是,我无法弄清楚为什么会这样。我也没有从互联网的其他地方找到任何东西。
MCVE:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Main
public static void main(String[] args)
int delay = 1000;
ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
ex.schedule(() ->
System.out.println("executed");
// ex.shutdown();
, delay, TimeUnit.MILLISECONDS);
lambdas 可能已经放弃了它,但这确实是 Java 8。
为什么任务执行后程序没有停止?
【问题讨论】:
你应该在提交所有任务后调用shutdown()
,而不是shutdownNow()
。这是根据ExecutorSErvice API。
@HovercraftFullOfEels 啊,是的,我知道。不过,这无关紧要。不用管它:)我会在那里编辑它anyhoo~
但shutdown()
调用并非无关,您的代码需要它。
@HovercraftFullOfEels 这无关紧要。这是一个很好的观点,但这不是我要问的。我在问为什么程序不会不调用shutdown
方法结束
但它非常相关。再次,请阅读我提供给您的 API 链接以了解原因。这是您的全部问题的全部答案。
【参考方案1】:
Executors#newSingleThreadScheduledExecutor()
返回的ScheduledExecutorService
线程池使用非守护线程。在您关闭线程池之前,这些仍然处于活动状态的等待任务。当非守护线程处于活动状态时,JVM 不会结束。
您可以使用重载的Executors#newSingleThreadScheduledExecutor(ThreadFactory)
并提供您自己的ThreadFactory
实现来创建守护线程。请注意,这可能会导致您的任务甚至可能无法运行,因为 JVM 会在任务的预定时间之前退出。
按照您的发现进行操作并关闭它。请注意,您应该始终在安全、操作不会失败的地方将其关闭。
【讨论】:
令人沮丧...仍然需要等待 3 分钟才能接受这个 当你在做的时候,你能提供一个更安全的地方的例子吗? @OlaviMustanoja 在不知道整个应用程序布局的情况下,这有点棘手。我已经改写了我的答案。我对 Swing(或一般的 UI 编程)知之甚少。 @SotiriosDelimanolis 假设提供的 MCVE 是我的整个应用程序,您能否提供一个简单的示例?我确信我可以稍后将这个示例应用到我的实际程序中。 @OlaviMustanoja 你的 MCVE 很好。我担心getWindow().closeWindow();
之类的东西可能是null
或者可能引发异常。在这种情况下,您需要有一个finally
块来执行shutdown
。同样,我不了解具有非命令行 GUI 的应用程序。您可能需要考虑使用System.exit(0)
关闭您的进程。【参考方案2】:
The Java Virtual Machine runs until all threads that are not daemon threads have died。 Executors.defaultThreadFactory()
将每个新线程创建为非守护线程。但是,如果您愿意朝那个方向冒险,Executors.newSingleThreadScheduledExecutor();
会以 ThreadFactory
作为参数的重载。
public static void main(String[] args)
int delay = 1000;
class DaemonFactory implements ThreadFactory
@Override
public Thread newThread(Runnable r)
Thread t = new Thread(r);
t.setDaemon(true);
return t;
ThreadFactory tf = new DaemonFactory();
ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor(tf);
ex.schedule(() ->
System.out.println("executed");
, delay, TimeUnit.MILLISECONDS);
【讨论】:
请注意,这可能会导致您的任务甚至可能无法运行,因为 JVM 会在任务预定时间之前退出。 @SotiriosDelimanolis 公平点。然而,当程序在程序之外不做任何事情(例如文件 IO、数据库连接等)时,它就没有意义了【参考方案3】:我会以完全不同的方式处理这个问题。你说:
我的程序中有一个计划任务,它在给定时间段后关闭一个框架。
为什么不为此使用 Swing Timer,因为它可以很好地与 Swing 事件线程配合使用?
new Timer(1000, new ActionListener()
public void actionPerformed(ActionEvent e)
((Timer) e.getSource()).stop();
someWindow.dispose();
).start();
【讨论】:
我在发布我的问题后实际上已经这样做了。然而,我决定不编辑我的帖子,因为我对ScheduledExecutorService
在任务执行后不让程序结束的原因很感兴趣。
对我来说,如果您在启动之前在Timer
上调用setRepeats(false)
,而不是让操作stop
成为计时器,它看起来会更干净,例如Timer t=new Timer(1000, e->someWindow.dispose()); t.setRepeats(false); t.start();
【参考方案4】:
您可以从 ScheduledExecutorService 调用关闭,因为它会等待线程执行,然后完成线程池。正如您在 Javadoc 中看到的:“启动有序关闭,其中执行先前提交的任务,但不会有新任务 被接受。如果已经关闭,则调用没有额外的效果。”
例子:
...
scheduledExecutorService.schedule(runnable, delay, TimeUnit.MILLISECONDS);
scheduledExecutorService.shutdown();
...
【讨论】:
【参考方案5】:我正在从 onCreate() 启动调度程序并在 onDestroy() 方法中停止它以停止调度程序服务。
public MyActivity extends Activity
ScheduledExecutorService scheduledExecutorService;
ScheduledFuture<?> scheduledFuture;
private int apiThreshold = 10;//seconds
onCreate()
startScheduler();
onDestroy()
if (scheduledFuture != null)
stopScheduler();
shutDownService();
super.onDestroy();
public void startScheduler()
Debug.e(TAG, "inside start scheduler");
scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new Runnable()
@Override
public void run()
// call method to do your task/perform your repeated task
, 4, apiThreshold, TimeUnit.SECONDS);
public void shutDownService()
if (scheduledExecutorService != null)
Log.e(“test,"in shutDown service close if not null");
scheduledExecutorService.shutdownNow(); // shutdown will allow the final iteration to finish
// executing where shutdownNow() will kill it immediately
Log.e(“test,"is service shutdown(true/false)=="+scheduledExecutorService.isShutdown());
【讨论】:
以上是关于ScheduledExecutorService - 单次操作后程序未结束的主要内容,如果未能解决你的问题,请参考以下文章
ScheduledExecutorService 定时器用法
ScheduledExecutorService - 单次操作后程序未结束
Java ScheduledExecutorService源码分析