Если вы используете фоновый поток, вызывайте Platform.runLater
с длительным Runnable
в качестве параметра, вы фактически ничего не добились. Runnable
все еще выполняется в потоке приложения JavaFX, замораживающем ваше приложение.
Вместо этого вы должны собрать все данные в фоновом потоке и обработать их до точки, где вам просто нужно настроить некоторые свойства сцены. Затем вы используете Platform.runLater
для выполнения этих обновлений.
Но хорошая новость заключается в том, что для этого сценария разработан класс, который может немного упростить ваш код: ScheduledService
.
Просто убедитесь, что вы не обращаетесь к GUI каким-либо образом из фонового потока (ни для чтения, ни для настройки свойств).
В следующем примере упрощенный пример должен демонстрировать общий подход. Он вычисляет несколько кратных значения, выбранного с помощью Spinner
в фоновом потоке с задержкой 10 секунд между каждым вычислением:
@Override
public void start(Stage primaryStage) {
Spinner<Integer> spinner = new Spinner(1, 100, 1);
// ensure the value is available in a way that allows synchronisation
final AtomicReference<Integer> input = new AtomicReference<>(spinner.getValue());
spinner.valueProperty().addListener((o, oldValue, newValue) -> input.set(newValue));
final int outputCount = 10;
GridPane root = new GridPane();
root.add(spinner, 0, 0, 2, 1);
// create output grid
Text[] output = new Text[outputCount];
for (int i = 1; i <= output.length; i++) {
Text text = new Text(Integer.toString(spinner.getValue() * i));
output[i - 1] = text;
root.addRow(i, new Text("Value multiplied by " + i + " = "), text);
}
root.setPrefWidth(300);
ScheduledService<int[]> service = new ScheduledService<int[]>() {
@Override
protected Task<int[]> createTask() {
return new Task<int[]>() {
@Override
protected int[] call() throws Exception {
// retrieve value and set it to null to denote a change
// that was already handled to avoid doing unnecessary
// work
Integer value = input.getAndSet(null);
int[] result = null;
if (value != null) {
int valueAsInt = value;
result = new int[outputCount];
for (int i = 0; i < outputCount; i++) {
result[i] = (i + 1) * valueAsInt;
}
}
// simpulate delay
Thread.sleep(2000);
return result;
}
};
}
};
service.valueProperty().addListener((o, oldValue, newValue) -> {
// update GUI
if (newValue != null) {
for (int i = 0; i < outputCount; i++) {
output[i].setText(Integer.toString(newValue[i]));
}
}
});
service.setPeriod(Duration.seconds(10));
// make sure service uses a daemon thread
service.setExecutor(Executors.newSingleThreadScheduledExecutor((Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}));
service.start();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
Я рекомендую просмотреть javadoc ScheduledService
, чтобы ознакомиться с его возможностями. Он также позволяет реагировать на исключения в задаче и указывать стратегию отсрочки.