如果 FX 线程在 Java 中终止,则终止线程的最有效方法
Most efficient way to terminate a Thread if an FX thread is terminated in Java
我编写了一个简单的 JavaFX 应用程序,显然 运行 在 FX 应用程序线程上。该应用程序需要在我调用 Platform.runLater()[ 的单独线程(不是 FX 线程)上的无限循环 运行 中进行一些后台处理=25=] 在固定时间间隔后更新 FX 应用程序的 gui 控件。
如果我关闭 FX Gui 应用程序,后台线程将继续执行。
为了在FX线程终止后终止后台线程,我现在使用fxThread.isAlive()后台线程上的 while 循环。
这样,当 FX 线程因 while 循环条件变为假而终止时,后台线程会自动终止。
这是一个错误的选择吗?完成相同任务的替代和有效方法是什么?
//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();
}
通过调用 fxThread.isAlive()
它不是最好的解决方案,因为在最坏的情况下,您的 fxThread
可能会死掉,而线程已经通过 fxThread.isAlive()
并且及时输入 Platform.runLater
会给你一个例外,除非这是你的情况的正确终止
尝试为顶级阶段的关闭事件添加一个侦听器。
同时调用 System.exit(0) 以完全终止 JVM 或您想要的任何自定义终止方法(f.e。如果后台线程仍然 运行,则显式中断后台线程) .
@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 的评论,该场景暗示生成线程不会成为守护线程。如果任何线程被标记为 daemon ,它将需要自定义终止/处理。
如果线程不需要在终止时进行任何清理,那么只需将该线程设置为守护线程即可:
Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads.
This method must be invoked before the thread is started.
使用 setDaemon()
是迄今为止最简单的方法,也是推荐的方法 如果您的处理线程不需要进行任何清理(例如,它不需要需要在应用程序退出之前完成或回滚原子提交事务)。
如果需要在线程退出前进行清理,那么最好不要让线程成为守护进程,而是让线程可中断,发出中断并处理中断。
例如,使用类似于 ExecutorService javadoc 中提到的方法的 ExecutorService
, that you shutdown in the 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() 调用隐式地向您的线程发送中断,除非您的线程处理器被显式编码为处理中断,否则这不会有效。
typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
如果取消实在不行,只想放弃,可以把上面代码中的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,那么您不想像上面那样吞下中断,而是想恢复中断状态,类似到下面(阅读下面的 "Dealing with 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();
}
}
}
另请参阅一些参考文档(此答案中的一些信息是从中复制和粘贴的):
- What is Daemon thread in Java?
- Dealing with InterruptedException
- javafx.concurrent.Task javadoc - 提供有关守护程序状态设置和其他机制的有用信息,用于在 JavaFX 应用程序上下文中实现可取消任务。
我编写了一个简单的 JavaFX 应用程序,显然 运行 在 FX 应用程序线程上。该应用程序需要在我调用 Platform.runLater()[ 的单独线程(不是 FX 线程)上的无限循环 运行 中进行一些后台处理=25=] 在固定时间间隔后更新 FX 应用程序的 gui 控件。 如果我关闭 FX Gui 应用程序,后台线程将继续执行。
为了在FX线程终止后终止后台线程,我现在使用fxThread.isAlive()后台线程上的 while 循环。 这样,当 FX 线程因 while 循环条件变为假而终止时,后台线程会自动终止。
这是一个错误的选择吗?完成相同任务的替代和有效方法是什么?
//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();
}
通过调用 fxThread.isAlive()
它不是最好的解决方案,因为在最坏的情况下,您的 fxThread
可能会死掉,而线程已经通过 fxThread.isAlive()
并且及时输入 Platform.runLater
会给你一个例外,除非这是你的情况的正确终止
尝试为顶级阶段的关闭事件添加一个侦听器。
同时调用 System.exit(0) 以完全终止 JVM 或您想要的任何自定义终止方法(f.e。如果后台线程仍然 运行,则显式中断后台线程) .
@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 的评论,该场景暗示生成线程不会成为守护线程。如果任何线程被标记为 daemon ,它将需要自定义终止/处理。
如果线程不需要在终止时进行任何清理,那么只需将该线程设置为守护线程即可:
Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads. This method must be invoked before the thread is started.
使用 setDaemon()
是迄今为止最简单的方法,也是推荐的方法 如果您的处理线程不需要进行任何清理(例如,它不需要需要在应用程序退出之前完成或回滚原子提交事务)。
如果需要在线程退出前进行清理,那么最好不要让线程成为守护进程,而是让线程可中断,发出中断并处理中断。
例如,使用类似于 ExecutorService javadoc 中提到的方法的 ExecutorService
, that you shutdown in the 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() 调用隐式地向您的线程发送中断,除非您的线程处理器被显式编码为处理中断,否则这不会有效。
typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
如果取消实在不行,只想放弃,可以把上面代码中的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,那么您不想像上面那样吞下中断,而是想恢复中断状态,类似到下面(阅读下面的 "Dealing with 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();
}
}
}
另请参阅一些参考文档(此答案中的一些信息是从中复制和粘贴的):
- What is Daemon thread in Java?
- Dealing with InterruptedException
- javafx.concurrent.Task javadoc - 提供有关守护程序状态设置和其他机制的有用信息,用于在 JavaFX 应用程序上下文中实现可取消任务。