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源码分析

关闭 ScheduledExecutorService

如何使用 ScheduledExecutorService 更改重复任务的速率或周期? [复制]

如何使用 ScheduledExecutorService 每天在特定时间运行特定任务?