我有一个使用线程和JavaFX库的任务。需要通过按下按钮启动单独的线程1。该线程应该满足一个具有整数值(100 - 150)且间隔很小(例如5 ms)的列表。
生成值后,应该停止。新线程2必须启动并使用生成的值填充ViewList。
但是每当线程1将新值添加到我的列表中时,我都会收到异常:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
at javafx.scene.Scene.addToDirtyList(Scene.java:485)
at javafx.scene.Node.addToSceneDirtyList(Node.java:424)
at javafx.scene.Node.impl_markDirty(Node.java:415)
我尝试在按钮事件侦听器中使用Platform.runLater()
而不是创建新的Thread,但程序在这种情况下停止响应。
有没有人可以帮助我,如何在单独的线程中使用值填充集合以及如何在第一个线程完成后启动第二个更新ViewList元素?
这是我的班级:
int itemName = 100;
Button btnOne = new Button("Button one");
Label label = new Label("Press the button to start");
ObservableList<String> listOptions = FXCollections.observableArrayList();
ListView<String> list;
@Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Hurry");
list = new ListView<>(listOptions);
list.setPrefSize(120, 120);
MultipleSelectionModel<String> lvSelModel =
list.getSelectionModel();
FlowPane rootNode = new FlowPane(10, 10);
rootNode.setAlignment(Pos.CENTER);
Scene myScene = new Scene(rootNode, 180, 200);
primaryStage.setScene(myScene);
lvSelModel.selectedItemProperty().addListener(
(changed, oldValue, newValue) -> {
label.setText("List option selected: " + newValue);
});
rootNode.getChildren().addAll(btnOne, list, label);
primaryStage.show();
Task<Integer> threadOne = new Task<Integer>(){
@Override
protected Integer call() throws Exception{
while(itemName < 130){
final int finalValue = itemName++;
listOptions.add(String.valueOf(itemName));
Platform.runLater(
() -> label.setText("Generating: " + finalValue));
Thread.sleep(100);
}
label.setText("Thread 1 finished");
return itemName;
}
};
btnOne.setOnAction(
ae -> {
Thread th = new Thread(threadOne);
th.setDaemon(true);
th.start();
});
}
public static void main(String[] args) {
launch(args);
}}`
结果程序应该是这样的:
感谢大家的时间!
答案 0 :(得分:2)
由于listOptions
是列表视图中的项目列表,因此调用listOptions.add(...)
会修改UI(列表视图)。因此,它需要在FX应用程序线程上执行,类似于设置标签的文本。只需将该行移到Platform.runLater(...)
:
Task<Integer> threadOne = new Task<Integer>(){
@Override
protected Integer call() throws Exception{
while(itemName < 130){
final int finalValue = itemName++;
Platform.runLater(() -> {
listOptions.add(String.valueOf(itemName));
label.setText("Generating: " + finalValue);
});
Thread.sleep(100);
}
label.setText("Thread 1 finished");
return itemName;
}
};
要在第一个任务完成时启动另一个任务(或者确实做任何事情),请使用任务的onSucceeded
处理程序:
threadOne.setOnSucceeded(e -> {
// create another task and run it in another thread...
});