Java 即使使用任务&;Platform.runLater(完整示例)

Java 即使使用任务&;Platform.runLater(完整示例),java,multithreading,user-interface,javafx,task,Java,Multithreading,User Interface,Javafx,Task,我读过类似的问题,但当我向VBox添加许多节点时,我的UI仍然冻结。我在下面提供了一个功能齐全的程序,它清楚地演示了这个问题 4秒后,ProgressIndicator冻结,因为有5000个节点添加到VBox。尽管使用Task(用于非UI工作)然后使用Platform.runLater()将节点添加到场景中,但用于演示JavaFX线程冻结的数量过多 private void addNodesToUI(VBox mainBox) { final int[] i = {0}; Ac

我读过类似的问题,但当我向
VBox
添加许多节点时,我的UI仍然冻结。我在下面提供了一个功能齐全的程序,它清楚地演示了这个问题

4秒后,
ProgressIndicator
冻结,因为有5000个节点添加到
VBox
。尽管使用Task(用于非UI工作)然后使用
Platform.runLater()
将节点添加到场景中,但用于演示JavaFX线程冻结的数量过多

private void addNodesToUI(VBox mainBox) {
    final int[] i = {0};

    Accordion temp = new Accordion();

    Platform.runLater(() -> {
        mainBox.getChildren().add(temp);
    });

    while (i[0] < 5000) {
        TitledPane tp = new TitledPane();
        tp.setPrefWidth(300);
        tp.setPrefHeight(12);
        tp.setPadding(new Insets(10));
        tp.setStyle("-fx-background-color: red;");

        i[0]++;

        Platform.runLater(() -> {
            temp.getPanes().add(tp);
        });
    }
}
在我的实际应用程序中,我不是添加空白的
标题栏
s,而是添加一个
标题栏
,该标题栏通过
新建FXMLLoader()
,从FXML文件中获得,然后生成的
loader.load()
初始化相关控制器,这反过来初始化了一些要求适中的计算——这些计算是在JavaFX线程上执行的!因此,即使我添加了接近250个节点,当最终使用
Platform.runLater
时,UI仍然冻结。在显示红色背景之前,如何防止
ProgressIndicator
冻结

完整示例:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Timer;
import java.util.TimerTask;

public class FreezingUI extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        VBox mainBox = new VBox();
        mainBox.setPrefHeight(800);
        mainBox.setStyle("-fx-background-color: #f1f1f1; -fx-alignment: center");
        Label label = new Label();
        label.setMinHeight(50);
        label.setStyle("-fx-font-size: 24px; -fx-text-fill: #515151");

        ProgressIndicator progressIndicator = new ProgressIndicator(ProgressIndicator.INDETERMINATE_PROGRESS);
        mainBox.getChildren().addAll(progressIndicator, label);

        Scene scene = new Scene(mainBox, 500, 800);
        primaryStage.setScene(scene);
        primaryStage.show();

        Timer timer = new Timer();
        TimerTask task = new TimerTask(){
            private int i = 4;
            public void run(){
                if (i >= 0) {
                    Platform.runLater(()->{
                        label.setText("Freezing in " + i--);
                    });
                }else{
                    addNodesToUI(mainBox);
                    timer.cancel();
                }
            }
        };
        timer.scheduleAtFixedRate(task, 0, 1000);
    }

    private void addNodesToUI(VBox mainBox) {
        final int[] i = {0};
        Platform.runLater(() -> {
            Accordion temp = new Accordion();
            mainBox.getChildren().add(temp);
            while (i[0] < 5000) {

                TitledPane tp = new TitledPane();
                tp.setPrefWidth(300);
                tp.setPrefHeight(12);
                tp.setPadding(new Insets(10));
                tp.setStyle("-fx-background-color: red;");
                temp.getPanes().add(tp);
                i[0]++;

            }

        });
    }
}
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.geometry.Insets;
导入javafx.scene.scene;
导入javafx.scene.control.Accordion;
导入javafx.scene.control.Label;
导入javafx.scene.control.ProgressIndicator;
导入javafx.scene.control.TitledPane;
导入javafx.scene.layout.VBox;
导入javafx.stage.stage;
导入java.util.Timer;
导入java.util.TimerTask;
公共类FreezingUI扩展了应用程序{
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage){
VBox mainBox=新的VBox();
主机箱设置高度(800);
mainBox.setStyle(“-fx背景色:#f1f1;-fx对齐:中心”);
标签=新标签();
标签设置最小高度(50);
label.setStyle(“-fx字体大小:24px;-fx文本填充:#515151”);
ProgressIndicator ProgressIndicator=新的ProgressIndicator(ProgressIndicator.不确定的进度);
mainBox.getChildren().addAll(progressIndicator,标签);
场景=新场景(主框,500800);
初级阶段。场景(场景);
primaryStage.show();
定时器=新定时器();
TimerTask任务=新的TimerTask(){
私人int i=4;
公开募捐{
如果(i>=0){
Platform.runLater(()->{
label.setText(“冻结在”+i--)中;
});
}否则{
addNodesToUI(mainBox);
timer.cancel();
}
}
};
timer.scheduleAtFixedRate(任务,0,1000);
}
专用void addNodesToUI(VBox mainBox){
final int[]i={0};
Platform.runLater(()->{
手风琴温度=新手风琴();
mainBox.getChildren().add(temp);
而(i[0]<5000){
标题窗格tp=新标题窗格();
tp.setPrefWidth(300);
tp.setPrefHeight(12);
tp.设置填充(新插图(10));
tp.setStyle(“-fx背景色:红色;”);
temp.getPanes().add(tp);
i[0]++;
}
});
}
}

之所以会发生这种情况,是因为您要求UI线程一次性完成大量任务。在创建所有5000个节点并将其添加到场景之前,UI线程无法退出
while
循环

private void addNodesToUI(VBox mainBox) {
    final int[] i = {0};

    Accordion temp = new Accordion();

    Platform.runLater(() -> {
        mainBox.getChildren().add(temp);
    });

    while (i[0] < 5000) {
        TitledPane tp = new TitledPane();
        tp.setPrefWidth(300);
        tp.setPrefHeight(12);
        tp.setPadding(new Insets(10));
        tp.setStyle("-fx-background-color: red;");

        i[0]++;

        Platform.runLater(() -> {
            temp.getPanes().add(tp);
        });
    }
}
private void addNodesToUI(VBox mainBox){
final int[]i={0};
手风琴温度=新手风琴();
Platform.runLater(()->{
mainBox.getChildren().add(temp);
});
而(i[0]<5000){
标题窗格tp=新标题窗格();
tp.setPrefWidth(300);
tp.setPrefHeight(12);
tp.设置填充(新插图(10));
tp.setStyle(“-fx背景色:红色;”);
i[0]++;
Platform.runLater(()->{
temp.getPanes().add(tp);
});
}
}
这将允许以小批量创建节点。通过这种方式,UI线程可以尝试在逐步添加节点的同时呈现UI


对于FXML,您可以在另一个线程中创建并加载FXML。仅当将场景分支附加到场景中时,才需要处于UI线程中。但是,我怀疑这只会减轻影响,因为您仍然需要一次性附加一大块内容。

您的代码存在编译问题。从最后一个
平台内部看不到
temp
手风琴。请稍后运行
调用。@jkostikidas已编辑。我没有从IDE复制,所以我犯了一个错误。没关系,我只是提到了它,以便您可以修复它;)做得好!现在速度肯定更快了,我同意你关于隔离非UI任务的建议!谢谢:)@Mathomatic有两件事要记住。首先,不要在UI线程中创建一个100年后永远不会存在的循环;把事情分解成小块,然后循序渐进。其次,在将节点附加到场景图中时,您只需要处于UI线程中。您可以在另一个线程中创建节点和其他计算内容。