JavaFX - 线程挂起,我无法弄清楚如何在UI线程之外运行我的循环

时间:2015-05-30 15:03:33

标签: java multithreading javafx

朋友。 stackoverflow的新功能,此处以及JavaFX。抱歉,如果我搞砸了任何术语,如果这有点啰嗦,但我确实把我的代码简化为最小的工作"例。 (除非你注释掉while循环,否则下面的代码会挂断,如代码本身所述)

我试图弄清楚如何在点击"唤醒"后不断改变其值Slider。单击" sleep"按钮并停止更改值按钮。为了论证,假设它随机地将值改变为0到99之间的整数。

从我read far所以match(我已经阅读了很多,但这些是最近的两个例子),似乎我应该可以使用Platform.runLater()updateMessenger()以避免在UI线程上运行无限直到我的睡眠按钮 - 中断循环。到目前为止,我没有实施前者,我仍然不确定如何实施后者。

我还尝试了解属性绑定,以便我可以在更改中间变量时更改UI线程中的Slider.valueProperty更改(更改UI线程中的UI元素有意义......对吗?)我在后台线程上调用state并从类stateObject创建。在后台线程计算我想要给滑块的值后,它会更改state的值,该值绑定到Slider的值。我认为将值计算分离为后台线程并且Slider值更改为UI线程可以解决问题,但我不断得到挂起或臭名昭着的IllegalStateException异常,Not on FX application thread

我并不完全确定Platform.runLater()魔法是如何工作的,但我仍然得到一个挂机,无论我在其中运行我的runnable接口还是Thread()

对于为什么这些尝试失败/如何使事情有效的任何帮助或解释将不胜感激。

干杯, 麦克

import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;

//Not sure which of these, if any, I actually need
import javafx.event.*;
import javafx.concurrent.*;
import javafx.beans.property.*;

public class MWE extends Application {

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

    //Class fields
    Button sleepBtn, wakeBtn;
    Slider displaySldr;
    boolean wakeful = true;

    //state property class
    class stateObject {
        //Store the property
        private DoubleProperty state = new SimpleDoubleProperty();
        //Define getter for property value
        public final double getState() {
            return state.get();
        }
        //Define setter for property value
        public final void setState(double val) {
            state.setValue(val);
        }
        //Define getter for property itself
        public DoubleProperty stateProperty() {
            return state;
        }
    }

    //Create state
    stateObject state = new stateObject();

    //Start method
    @Override
    public void start(Stage primaryStage) {

        //Create top-level pane to store nodes into for the scene
        VBox mainPane = new VBox(20);
        mainPane.setAlignment(Pos.CENTER);

        //Create buttons
        sleepBtn = new Button("Sleep");
        sleepBtn.setOnAction(e -> NPSleep());
        wakeBtn = new Button("Wake");
        wakeBtn.setOnAction(e -> NPWake());

        //Lay out buttons
        VBox buttonsBox = new VBox(10);
        buttonsBox.getChildren().addAll(wakeBtn,sleepBtn);
        buttonsBox.setAlignment(Pos.CENTER);

        //Create slider to be change continuously by clicking wakeBtn once
        //Slider should stop changing when sleepBtn is clicked
        displaySldr = new Slider(0,100,50);
        displaySldr.setMajorTickUnit(25);
        displaySldr.setMinorTickCount(24);
        displaySldr.setSnapToTicks(true);
        displaySldr.setShowTickMarks(false);
        displaySldr.setShowTickLabels(true);

        //Make property event. When 'state' changes, change displaySldr value
        state.stateProperty().addListener(
            (observable,oldvalue,newvalue) ->
                    displaySldr.setValue(newvalue.intValue())
        );


        //Organize labels and sliders
        VBox LSPane = new VBox(10);
        LSPane.getChildren().addAll(displaySldr);
        LSPane.setAlignment(Pos.CENTER);

        //Organize sub-panes into top-level pane, mainPane
        mainPane.getChildren().addAll(LSPane,buttonsBox);

        //Set the scene
        Scene mainScene = new Scene(mainPane);

        //Set the stage
        primaryStage.setTitle("oh god please work");
        primaryStage.setScene(mainScene);
        primaryStage.show();

    }

    private void NPWake() {

        Runnable wakeyWakey = () -> {
            while (wakeful == true) { //Commenting out the while loop and 
                                        //leaving the command allows program to run fine
                                        //but slider changes only once, not continuously
                state.setState( Math.floor(Math.random()*100) );

                try{
                    Thread.sleep(300);
                } catch (InterruptedException ie) {
                }
            }
        };

        Platform.runLater(wakeyWakey);
//        Thread wakeThread = new Thread(wakeyWakey); //
//        wakeThread.start();                         // This doesn't work either
    }


    private void NPSleep() {
        if (wakeful == true) {
            wakeful = false;
        }
    }

}

1 个答案:

答案 0 :(得分:1)

您不需要State Object,因为您仍然必须更新JavaFX Application Thread上的值,因此它无法创建此类对象。您可以删除所有State Object相关代码。以下代码是您的解决方案的修复程序:

private void NPWake() {
    Thread t = new Thread(() -> {
        while (wakeful) {
            try {
                Platform.runLater(() -> {
                    displaySldr.setValue(Math.floor(Math.random() * 100));
                });

                Thread.sleep(300);
            }
            catch (Exception e) {}
        }
    });
    t.start();
}
onAction处理Button的{​​{1}}并且在任何情况下我们都不希望在该线程上运行无限循环,除非它是它自己的内部,因为循环将简单地挂起那个帖子。因此,我们创建一个JavaFX Application Thread,它将具有该循环,并在其正文中调用new Thread t来提交更改。如您所收集的,必须在JavaFX Application Thread上更改UI / JavaFX属性。 JavaFX Application Thread表示“某些代码”将在未来某个时间点在Platform.runLater( "some code" )上执行(如果线程不忙,几乎立即执行)。

至于此类应用程序的设计,我建议您查看JavaFX Application Thread - https://docs.oracle.com/javase/8/javafx/api/javafx/animation/AnimationTimer.htmlAnimationTimer - https://docs.oracle.com/javase/8/javafx/api/javafx/animation/Timeline.html。它们都运行在Timeline