JavaFX 中的多线程会挂起 UI

Posted

技术标签:

【中文标题】JavaFX 中的多线程会挂起 UI【英文标题】:Multithreading in JavaFX hangs the UI 【发布时间】:2013-01-18 10:00:05 【问题描述】:

我有一个简单的 JavaFX 2 应用程序,有 2 个按钮,分别是“开始”和“停止”。单击开始按钮时,我想创建一个后台线程,该线程将进行一些处理并随着它的进行更新 UI(例如进度条)。如果单击停止按钮,我希望线程终止。

我尝试使用从文档中收集到的javafx.concurrent.Task 类来执行此操作,这可以正常工作。但是,每当我单击“开始”时,UI 就会冻结/挂起,而不是保持正常。

她是来自 Myprogram extends Application 主类的代码,用于显示按钮:

public void start(Stage primaryStage)
               
    final Button btn = new Button();
    btn.setText("Begin");

    //This is the thread, extending javafx.concurrent.Task :
    final MyProcessor handler = new MyProcessor();
    btn.setOnAction(new EventHandler<ActionEvent>()
    
        public void handle(ActionEvent event)
                        
           handler.run(); 
        
    );

    Button stop = new Button();
    stop.setText("Stop");
    stop.setOnAction(new EventHandler<ActionEvent>()
        
             public void handle(ActionEvent event)
             
                handler.cancel();
             
        

    );
    // Code for adding the UI controls to the stage here.

这是MyProcessor类的代码:

import javafx.concurrent.Task;
public class MyProcessor extends Task
   
    @Override
    protected Integer call()
    
        int i = 0;
        for (String symbol : feed.getSymbols() )
        
            if ( isCancelled() )
            
                Logger.log("Stopping!");
                return i;
            
            i++;
            Logger.log("Doing # " + i);
            //Processing code here which takes 2-3 seconds per iteration to execute
            Logger.log("# " + i + ", DONE! ");            
        
        return i;
    

非常简单,但是每当我单击“开始”按钮时 UI 就会挂起,尽管控制台消息会继续显示(Logger.log 只是 System.out.println

我做错了什么?

【问题讨论】:

【参考方案1】:

Task 实现了Runnable,因此当您调用handler.run(); 时,您实际上是在UI 线程中运行call 方法。这将挂起 UI。

您应该在后台线程中启动任务,通过执行程序或简单地调用new Thread(handler).start();

这在the javadoc 或JavaFX concurrency tutorial 中有解释(可能不是很清楚)。

【讨论】:

我还有一个问题。我想用任务线程的进度更新主 UI 中的标签。为此,我将在任务中使用此代码:mainProg.updateStatus(i);,并在主程序中:public void updateStatus(int i) this.label.setText(i) ); 。这行得通,还是我应该使用MainProg.updateStatus() 中的platform.runLater() 来更新用户界面? 是的,您需要使用 runLater 在 UI 线程中运行它 - 或者,您可以使用将在 UI 线程中运行的 updateProgress 方法(参见教程中的示例 3)。 对于那些在寻找如何绑定后端任务和 UI 以便后端更新 UI 的答案的人,我推荐这篇文章:oracle.com/technetwork/articles/java/enterprisefx-1651840.html。它以某种方式比每次 Google 搜索都会出现的“JavaFX 并发教程”更好地解释了主题。 @TaaviIlves 您的链接有何相关性? “学习使用 LightView 将 REST 服务转换为一组可绑定的属性”。 只是为了解释与后端和前端的通信,工作示例更详细。对于特定的问题和答案,它确实不那么相关。我不是故意误导,但是很多谷歌搜索总是把我带到“JavaFX 并发教程”,这对我没有多大帮助。

以上是关于JavaFX 中的多线程会挂起 UI的主要内容,如果未能解决你的问题,请参考以下文章

GDI+ 多线程绘图

为啥在不同线程中调用 asyncio subprocess.communicate 会挂起?

Java JDBC Oracle SQL 查询每隔几个月就会挂起一次

Android UI 在 JDBC 连接上挂起 - 即使连接在另一个线程上

多线程挂起UI?

通过wildfly将实体持久化到postgresql期间挂起的多线程事务