JavaFX FPS上限为60 FPS
人们似乎普遍认为javafxui线程每秒更新60次(,)。我的理解是,更新意味着JavaFX FPS上限为60 FPS,java,multithreading,javafx,Java,Multithreading,Javafx,人们似乎普遍认为javafxui线程每秒更新60次(,)。我的理解是,更新意味着pulse 脉冲是一个事件,它向JavaFX场景图指示 是同步场景图上元素状态的时间 用棱镜。脉冲的最大速度为每秒60帧(fps) 并在场景图形上运行动画时激发。即使 当动画未运行时,当出现某个对象时,会计划一个脉冲 场景图形已更改。例如,如果按钮的位置为 更改后,将计划一个脉冲 资料来源: 为了弄清楚如果对Platform.runLater的调用超过60次会发生什么,我编写了这个小程序: import javafx
pulse
脉冲是一个事件,它向JavaFX场景图指示
是同步场景图上元素状态的时间
用棱镜。脉冲的最大速度为每秒60帧(fps)
并在场景图形上运行动画时激发。即使
当动画未运行时,当出现某个对象时,会计划一个脉冲
场景图形已更改。例如,如果按钮的位置为
更改后,将计划一个脉冲
资料来源:
为了弄清楚如果对Platform.runLater的调用超过60次会发生什么,我编写了这个小程序:
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被堆叠起来,UI线程穿插运行,然后执行所有排队的Runnable
- UI线程执行前60个调用,然后进一步排队
Runable
s up
但是,在一段初始延迟之后,当可运行程序排队时,UI线程一次处理完所有可运行程序时,两个线程按顺序运行:
任务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次脉冲上限的概念
- 这个小的执行片段是否没有那么重,因此可能会超出限制
我首先想知道的是,如果UI线程达到其上限,那么推送到UI线程上的Runable
s会发生什么情况。是否所有排队的都可以运行
在UI线程的一次运行中按顺序执行,还是只有一个可以运行
执行,其余的必须等待?第二种情况会导致严重的问题,当在UI线程上持续推送超过该限制的可运行的时。脉冲上限为60fps<提交到FX应用程序线程的代码>可运行
s不可用
据我所知,有两个线程:FX应用程序线程和prism渲染线程。FX应用程序线程使用队列中的Runnable
s并执行它们。prism渲染线程每秒最多执行60次,与FX应用程序线程同步(从而暂时阻止它执行新的可运行程序)并渲染帧,然后释放FX应用程序线程
因此,FX应用程序线程将尽快执行您的Runnable
,并且不限于每1/60秒运行一次;但在帧渲染期间,它不会从队列中获取新的可运行文件。(FX应用程序线程上的用户事件处理方式与您发布的Runnable
s类似。)
所以有两种方法可以让坏事发生。一种是在FX应用程序线程上(无论是在事件处理程序中还是在Runnable
发布到Platform.runLater()
)需要很长时间才能执行的代码。执行此操作将阻止prism渲染线程与FX应用程序线程同步,这意味着在runnable完成之前无法渲染下一帧。另一种方法是用太多的Runnable
s轰击FX应用程序线程,这样队列的增长速度就超过了它们的消耗速度。(本质上,由于队列总是有可用的东西,因此在这些情况下,FX应用程序线程会变成一个繁忙的线程,并且不能保证prism渲染线程能够运行。)
因此,您的问题的直接答案是,Runnable
s按照发布的顺序执行,尽可能快地在单个线程上执行。帧渲染(上限为60 fps)将暂时停止使用队列中的Runnable
s
在伪代码中,我猜测(这只是我的直觉,它是如何工作的,这不是基于源代码)FX应用程序线程看起来像
while (fxToolkitRunning()) {
Runnable runnable = runnableQueue.take(); // block until something available
acquireLock();
runnable.run();
releaseLock();
}
棱镜渲染线程看起来像:
while (fxToolkitRunning()) {
while (! timeForNextFrame()) {
sleep(timeUntilNextFrame());
}
acquireFXApplicationThreadLock();
if (sceneNeedsUpdating()) {
renderFrame();
}
releaseFXApplicationThreadLock();
}