【问题标题】:Most efficient way to terminate a Thread if an FX thread is terminated in Java如果 FX 线程在 Java 中终止,则终止线程的最有效方法
【发布时间】:2017-05-05 00:43:48
【问题描述】:

我编写了一个简单的 JavaFX 应用程序,它显然在 FX 应用程序线程上运行。应用程序需要在一个单独的线程(不是 FX 线程)上运行的无限循环中进行一些后台处理,我在其中调用 Platform.runLater() 来更新 FX 的 gui 控件固定间隔后应用。 如果我关闭 FX Gui 应用程序,后台线程会继续执行。

为了在 FX 线程终止后终止后台线程,我现在在后台线程的 while 循环中使用 fxThread.isAlive()。 这样一来,当 FX 线程在 while 循环条件变为 false 时终止时,后台线程就会自动终止。

这是一个糟糕的选择吗?完成相同任务的替代和有效方法是什么?

//imports
public class SimpleClockFX implements Application{  
Thread fxThread;  
 //other variables  

@Override  
public void start(Stage primaryStage){  
    fxThread = Thread.currentThread();  

    //other stuff...  

    new Thread(()->{  
    //logic...  
      while(fxThread.isAlive()){  
          //logic...  
                  Platform.runLater(()->{
                      //update gui controls
                  });
      }  
    }).start();  

}

【问题讨论】:

  • 可能是codereview.stackexchange.com的问题
  • 如何终止 FX 应用程序,以及如何启动后台线程?我也想你没有通过thread.setDaemon(true)
  • 请在您的问题中发布更改,而不是在 cmets 中
  • 主类正在实现 javafx.application.Application。在被覆盖的方法 start(Stage stage) 中,我将后台线程创建为: new Thread(()->{//logic here}).start();
  • 实际上评论是在完成前按回车键意外发布的......所以:)

标签: java multithreading javafx terminate background-thread


【解决方案1】:

通过调用fxThread.isAlive() 它不是最好的解决方案,因为在最坏的情况下,你的fxThread 可能会死,而线程已经通过fxThread.isAlive() 并且在输入Platform.runLater 的时候会给你一个例外,除非这是您案件的适当终止

尝试在顶级舞台上为关闭事件添加监听器。

还调用 System.exit(0) 以完全终止 JVM 或您想要的任何自定义终止方法(例如,如果后台线程仍在运行,则显式地中断后台线程)。

    @Override
    public void start(Stage primaryStage) 
    {
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            public void handle(WindowEvent we) {
                System.out.println("Stage is closing");
                System.exit(0);
            }
        });  

        primaryStage.setTitle("Hello World!");
//      add your components
        primaryStage.show();
//      not daemon
        new Thread(new CustomRunnable()).start();

    }

    private static class CustomRunnable implements Runnable
    {
        public void run() 
        {

            while(true){
//              long operation
            }
        }
    }

编辑:

根据@lostsoul29 cmets,该场景意味着生成线程不会是守护线程。如果任何线程被标记为 daemon ,则需要自定义终止/处理。

【讨论】:

  • 看起来不错....不知道 setOnCloseRequest() 存在。我刚刚添加了以下 sn-p:primaryStage.setOnCloseRequest((WindowEvent we) ->{ System.exit(0); });并将 while 循环条件更新为“true”......而且它的工作非常好。谢谢你:)
  • System.exit(0) 不会杀死所有生成的子线程。您必须依次关闭每个线程,然后退出应用程序。
  • @lostsoul29 ,看来您还没有很清楚地理解问题/答案。首先,您可以参考下面的链接,如果您仍然遇到问题,请告诉我们一个新问题。 Thread Life-Cycle , setDaemon()
【解决方案2】:

如果线程不需要在终止时进行任何清理,那么只需将该线程设为守护线程:

thread.setDaemon(true)

将此线程标记为守护线程或用户线程。当唯一运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在线程启动前调用。

使用setDaemon() 是迄今为止最简单的方法,并且是推荐的方法如果您的处理线程不需要进行任何清理(例如,它不需要完成或回滚一个原子提交事务)在应用程序退出之前。


如果需要在线程退出前对其进行清理,那么最好不要让线程成为守护进程,而是让线程可中断,发出中断并处理中断。

例如,使用ExecutorService 管理您的线程,您可以使用类似于 ExecutorService javadoc 中提到的方法在 Application stop() 方法中关闭该线程:

void shutdownAndAwaitTermination(ExecutorService pool) {
  pool.shutdown(); // Disable new tasks from being submitted
  try {
    // Wait a while for existing tasks to terminate
    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
      pool.shutdownNow(); // Cancel currently executing tasks
      // Wait a while for tasks to respond to being cancelled
      if (!pool.awaitTermination(60, TimeUnit.SECONDS))
          System.err.println("Pool did not terminate");
    }
  } catch (InterruptedException ie) {
    // (Re-)Cancel if current thread also interrupted
    pool.shutdownNow();
    // Preserve interrupt status
    Thread.currentThread().interrupt();
  }
}

请注意,shutdownNow() 调用会隐式向您的线程发送中断,除非您的线程处理器被显式编码以处理中断,否则这将无效。

典型的实现将通过 Thread.interrupt() 取消,因此任何未能响应中断的任务都可能永远不会终止。

如果取消确实不起作用而您只想放弃,可以将上述代码中的System.err.println() 语句替换为System.exit()。

而且你的线程任务逻辑也需要处理中断:

public class PrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;

    PrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted())
                queue.put(p = p.nextProbablePrime());
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() { interrupt(); }
}

另外请注意,如果您不是子类化 Thread,而是为库类型代码实现 Runnable,那么您不想像上面那样吞下中断,而是想恢复中断状态,类似于下面(阅读下面的“处理InterruptedException”以进一步了解为什么库代码需要恢复中断状态):

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;

    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }

    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         }
         catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

另请参阅一些参考文档(此答案中的一些信息是从中复制和粘贴的):

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-18
    • 2015-12-21
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2011-08-11
    相关资源
    最近更新 更多