我编写了一个简单的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();
}
答案 0 :(得分:3)
通过调用fxThread.isAlive()
这不是最好的解决方案,导致在最糟糕的情况下,您的fxThread
可能会死亡,而线程已经通过fxThread.isAlive()
并且在进入Platform.runLater
会给你一个例外,除非这是你案件的正确终止
尝试在顶级舞台上为close事件添加一个监听器。
同时调用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评论,该场景暗示产生线程不会是守护线程。如果任何线程被标记为守护进程,则需要自定义终止/处理。
答案 1 :(得分:3)
如果线程在终止时不需要进行任何清理,那么只需将该线程作为守护线程:
将此线程标记为守护程序线程或用户线程。当运行的唯一线程都是守护程序线程时,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();
}
}
}
另请参阅一些参考文档(此答案中的某些信息是复制和粘贴的):