Похоже, существует общее понимание того, что поток пользовательского интерфейса JavaFX поддерживается 60 обновлениями в секунду (1, 2). Я понимаю, что обновление означает pulse
.Крышка JavaFX FPS при 60 FPS
Импульс представляет собой событие, которое указывает на графе сцены JavaFX, что является время, чтобы синхронизировать состояние элементов на графе сцены с Prism. Импульс сжимается со скоростью 60 кадров в секунду (fps) максимум и запускается всякий раз, когда анимация выполняется на графике сцены. Даже , когда анимация не работает, импульс запланирован, когда что-то в изменяется график сцены. Например, если позиция кнопки изменена, запланирован импульс.
Источник: https://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm
Чтобы выяснить, что происходит, если есть, например, более 60 звонков в Platform.runLater
Я написал эту небольшую программу:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class HighUITraffic extends Application {
private int counter = 0;
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
long sheduleTime = System.currentTimeMillis();
System.out.println("Task "+counter+" run at "+sdf.format(new Date(sheduleTime)));
Platform.runLater(new RunInUI(counter));
counter++;
}
};
timer.schedule(task, 0, 10); // every 10ms => 100 per second
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private static class RunInUI implements Runnable {
private final int counter;
public RunInUI(int counter) {
this.counter = counter;
}
@Override
public void run() {
long executionTime = System.currentTimeMillis();
System.out.println("Task "+counter+" executed in Application Thread at "+sdf.format(new Date(executionTime)));
}
}
}
Мои ожидания были что либо:
Runnable
s уложены в стопку, а поток пользовательского интерфейса перемещается, а затем выполняет всеRunables
, которые поставили в очередь.- Поток пользовательского интерфейса выполняет первые 60 вызовов, а затем очереди
Runable
.
Однако то, что произошло после того, как какой-то первоначальной задержки, где Runables очередями и где потом все обрабатывается за один проход с помощью потока пользовательского интерфейса, в том, что два потока выполняются в порядке:
Task 1281 работать на 2016-01-25T18: 37: 00.269 Задача 1281, выполненная в . Тема приложения на 2016-01-25T18: 37: 00.269 Задача 1282, выполняемая в 2016-01-25T18: 37: 00.274 Задача 1282, выполненная в прикладной теме на этапе 2016- 01-25T18: 37: 00.274 Задача 1283 запускается в 2016-01-25T18: 37: 00.279
И было более 60 вызовов нитей в течение одной секунды (я пробовал с 100 и 200, без каких-либо различий).
Поэтому я смущен:
- ли я неправильно понять концепцию укупорки на 60 импульсов?
- Этот небольшой фрагмент выполнения не так сильно нагружен, чтобы предел мог быть превышен?
Что я хотел знать в первую очередь, так это то, что происходит с Runable
s, которые нажимаются на поток пользовательского интерфейса, если поток достиг предела лимита. Все ли Runable
s, которые поставили в очередь, выполнялись последовательно в одном прогоне потока пользовательского интерфейса или выполнялись только один Runable
, а остальное пришлось ждать? Второй случай вызовет серьезные проблемы, когда непрерывно нажимает больше Runable
с на поток пользовательского интерфейса, чем этот предел.