【问题标题】:ScheduledExecutorService - program not ending after one-shot actionScheduledExecutorService - 单次操作后程序未结束
【发布时间】: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 链接以了解原因。这是您的全部问题的全部答案。

标签: java multithreading scheduled-tasks java-8 scheduledexecutorservice


【解决方案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 diedExecutors.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());
            }
        }
    
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      • 1970-01-01
      • 1970-01-01
      • 2021-09-01
      相关资源
      最近更新 更多