multithreading - 如何从JavaFX中的单独线程更新状态和进度

标签 multithreading textarea javafx-2 output

我需要能够调用一个单独的线程。线程分析文件并从中获取统计信息。

文件分析最多可能需要2分钟,并且在分析过程中会将数据打印到日志中。

我想在前端有一个TextArea,它需要打印出分析结果(即分析结果),并且我还希望有一个进度条来指示进度。所有这些都在单独的线程中确定。

我所做的是在UI类中创建一个方法,以将一个字符串添加到Text Area并将该类的引用传递给启动的线程。

我的主类

package trymutilthread;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TryMutilThread extends Application {

    TextArea ta;

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

    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button();
        btn.setText("Start");
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                startScheduledExecutorService();
            }
        });

        ta = new TextArea();        

        VBox vBox = new VBox();
        vBox.getChildren().addAll(btn, ta);

        StackPane root = new StackPane();
        root.getChildren().add(vBox);

        Scene scene = new Scene(root, 300, 750);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void startScheduledExecutorService() {
        final TryMutilThread classI = this;

        Task<Void> task = new Task<Void>() {
            @Override protected Void call() throws Exception {
                try {
                    ta.appendText("Starting Thread\n");
                    new SomeProcess(classI).doTheLogic();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        };

        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();      
    }

    public void appendText(String string) {        
        ta.appendText(string);
    }
}   

在线程中执行的类
package trymutilthread;

public class SomeProcess {

    TryMutilThread taClass = null;

    public SomeProcess (TryMutilThread taClass) {
        this.taClass = taClass;
    }

    public void doTheLogic() throws Exception{        
        taClass.appendText("Staring Thread");        

        for (int i = 0; i < 5000; i++) {
            taClass.appendText(String.valueOf(i));
        }

        taClass.appendText("Ending Thread");
    }
}

现在,当我执行此操作时,线程结束后仍仅将文本输出到TextArea。

我确实看了以下2个帖子:

JavaFX update textArea

Java client / server thread null pointer exception when quickly communicating messages

在该过程结束之前,我无法将数据打印到日志中。

我更新了代码以创建任务。
但是现在执行时出现以下错误
Executing com.javafx.main.Main from F:\DEV\Projects\TryMutilThread\dist\run404234128\TryMutilThread.jar using platform C:\Program Files\Java\jdk1.7.0_10/bin/java
java.lang.NullPointerException
    at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.computeLinePadding(NGTextHelper.java:405)
    at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.access$200(NGTextHelper.java:292)
    at com.sun.javafx.sg.prism.NGTextHelper.buildTextLines(NGTextHelper.java:2357)
    at com.sun.javafx.sg.prism.NGTextHelper.validateText(NGTextHelper.java:1847)
    at com.sun.javafx.sg.prism.NGTextHelper.getCaretShape(NGTextHelper.java:1435)
    at javafx.scene.text.Text.getDecorationShapes(Text.java:1150)
    at javafx.scene.text.Text.impl_geomChanged(Text.java:757)
    at javafx.scene.text.Text$1.invalidated(Text.java:214)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:127)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
    at javafx.scene.text.Text.setText(Text.java:188)
    at com.sun.javafx.scene.control.skin.TextAreaSkin$17.invalidated(TextAreaSkin.java:610)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:359)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1034)
    at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1038)
    at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:978)
    at javafx.scene.control.TextInputControl$TextProperty.access$200(TextInputControl.java:950)
    at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:119)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.scene.control.TextArea$TextAreaContent.insert(TextArea.java:196)
    at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:373)
    at javafx.scene.control.TextInputControl.insertText(TextInputControl.java:308)
    at javafx.scene.control.TextInputControl.appendText(TextInputControl.java:298)
    at trymutilthread.TryMutilThread.appendText(TryMutilThread.java:80)
    at trymutilthread.SomeProcess.doTheLogic(SomeProcess.java:26)
    at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:66)
    at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:62)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.lang.Thread.run(Thread.java:722)

然后,我用Google搜索错误,似乎需要将交互代码放入Platform.runlater()中。

Java client / server thread null pointer exception when quickly communicating messages

我更改了类以将线程执行为
package trymutilthread;

import javafx.application.Platform;

public class SomeProcess {

    TryMutilThread taClass = null;

    public SomeProcess(TryMutilThread taClass) {
        this.taClass = taClass;
    }

    public void doTheLogic() throws Exception {
        taClass.appendText("Staring Thread");

        for (int i = 0; i < 5000; i++) {
            //remove this append line
            //taClass.appendText(i + "\n");                
            //And replaced it with platform.runlater
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    taClass.appendText("AGREED" + "\n");
                }
            });
        }

        taClass.appendText("Ending Thread");
    }
}

它执行时没有任何错误,但现在似乎又回到了开始... UI被冻结,直到所有内容都添加到TextArea中为止

最佳答案

问题在于,您正在使用过多的请求来充斥FX应用程序线程。在Platform.runLater(...)调用之间没有任何实际的工作发生。这个问题可能会在您的实际应用程序而不是此测试中消失,但是为了模仿实际的长时间运行的工作,您可以在其中放置一个Thread.sleep(...):

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TextAreaBackgroundUpdateExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        final BorderPane root = new BorderPane();
        final TextArea textArea = new TextArea();
        final ProgressBar progress = new ProgressBar();
        final Button startButton = new Button("Start");

        final int maxCount = 5000 ;

        startButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Task<Void> task = new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        for (int i = 1; i <= maxCount; i++) {
                            Thread.sleep(10);
                            final int count = i ;
                            Platform.runLater(new Runnable() {
                                @Override
                                public void run() {
                                    textArea.appendText("Processed part " + count + " (of "+maxCount+")\n");                                    
                                }
                            });
                            updateProgress(i, maxCount);
                        }
                        return null;
                    }
                };
                progress.progressProperty().bind(task.progressProperty());
                Thread t = new Thread(task);
                t.setDaemon(true);
                t.start();
            }
        });

        root.setCenter(textArea);
        root.setTop(progress);
        root.setBottom(startButton);

        final Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


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

关于multithreading - 如何从JavaFX中的单独线程更新状态和进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20594825/

相关文章:

java - 更改折线图符号的颜色和线型

java - 在一个AsyncTask实例上多次调用execute()时,线程是否同步运行

multithreading - Observable 对不同线程上的队列更改没有反应

html - 带有 simple_form 的默认 Rails 文本区域值

php - Textarea 破坏了 MySQL 更新查询

JavaFX MediaPlayer 使用 mp4 创建数百个线程

java - 如何正确使用带套接字的 HandlerThread?

java - 如果调用 ScheduleWithFixedDelay 方法的次数超过池大小,ScheduledExecutorService 如何工作?

javascript - 使用 JavaScript 从隐藏控件复制文本

用于服务器端图像生成的 JavaFX