Javafx. Как запустить потоки после нажатия кнопки
После нажатия на кнопку запускаются два потока: первый меняет картинку у кнопки, второй -- цикл.
Я хочу, чтобы после нажатия сначала менялась картинка и показывалась на экране, а после запускался цикл, но всё завершается в один момент.
Я могу увидеть изменение картинки только после завершения цикла.
myButton.setOnMouseClicked(event -> {
if (event.getButton() == MouseButton.PRIMARY) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ImageView imageView = new ImageView(image);
imageView.setFitHeight(my_button_height - 16);
imageView.setFitWidth(my_button_width - 16);
myButton.setStyle("-fx-background-color: #ffffff00; ");
myButton.setGraphic(imageView);
}
});
thread.run();
try {
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 15; i++){
System.out.println(i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
thread1.run();
}
});
Ответы (1 шт):
Для запуска отдельного потока в общем случае используется метод Thread::start.
Если создать экземпляр класса-потомка Thread и просто вызвать его метод run, то код этого метода будет выполняться в основном потоке, то есть, в представленной реализации нет параллельного выполнения двух дополнительных потоков, а обычное последовательное выполнение, причем запуск кода thread1 вызовет "подвисание" после нажатия кнопки длительностью 7.5 с = 15 * 500 мс.
Также при создании потоков можно было использовать лямбду Thread t = new Thread(() -> {...}), а не многословное "старомодное" объявление.
Кроме того, в данном случае первый поток пытается изменить UI в JavaFX, что разрешено только из потока JavaFX приложения (JavaFX application thread).
Поэтому первый поток следует запускать при помощи Platform::runLater(Runnable r), соответственно, не потребуется вызывать Thread::join.
Исправленный код:
myButton.setOnMouseClicked(event -> {
if (event.getButton() == MouseButton.PRIMARY) {
Platform.runLater(() -> {
ImageView imageView = new ImageView(image);
imageView.setFitHeight(my_button_height - 16);
imageView.setFitWidth(my_button_width - 16);
myButton.setStyle("-fx-background-color: #ffffff00; ");
myButton.setGraphic(imageView);
});
// нет изменения UI, отдельная переменная `thread1` не нужна
new Thread(() -> {
for (int i = 0; i < 15; i++){
System.out.println(i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
})
.start(); // можно просто вызвать метод Thread::start
}
});
Связанные вопросы на основном SO:
- How can I create Thread in javafx
- Execute task in background in JavaFX -- здесь рассматривается пример более сложного взаимодействия фонового потока с UI при помощи создания
Task<Void>.