似乎有一个共同的理解是JavaFX UI线程每秒有60次更新(1,2)。据我了解,更新意味着pulse
。
脉冲是向JavaFX场景图指示它的事件 是时候同步场景图上元素的状态 与棱镜。脉冲以每秒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
被堆叠,UI线程散布,然后执行已排队的所有Runables
。Runable
。然而,在一些初始延迟之后发生了什么事情,其中Runables排队等待,然后由UI线程一次性处理,是两个线程按顺序运行:
任务1281运行于2016-01-25T18:37:00.269任务1281执行 应用程序线程在2016-01-25T18:37:00.269任务1282运行于 2016-01-25T18:37:00.274任务1282在Application Thread中执行 2016-01-25T18:37:00.274任务1283运行于2016-01-25T18:37:00.279
在一秒钟内有超过60个调用任何一个线程(我试过100和200,没有任何区别)。
因此我很困惑:
首先我想知道的是,如果线程达到其上限,那么推送到UI线程的Runable
会发生什么。排队的所有Runable
是否在UI线程的一次运行中按顺序执行,或者只执行了一次Runable
,其余的必须等待?第二种情况会导致严重的问题,因为在UI线程上持续推送的Runable
比该限制更多。{/ p>
答案 0 :(得分:5)
脉冲上限为60fps。提交给FX Application Thread的Runnable
不是。
据我了解,有两个主题:FX Application Thread和prism渲染线程。 FX应用程序线程从队列中消耗Runnable
并执行它们。棱镜渲染线程每秒最多执行60次,与FX应用程序线程同步(从而暂时阻止它执行新的runnables)并渲染帧,然后释放FX应用程序线程。
因此FX应用程序线程将尽快执行您的Runnable
,并且不限于每1/60秒运行一次;但是在渲染帧期间不会从队列中获取新的runnable。 (用户事件在FX应用程序主题上以与您发布的Runnable
类似的方式处理。)
所以有几种方法可以让坏事发生。一种是在FX应用程序线程上有代码(无论是在事件处理程序中还是在Runnable
中发布到Platform.runLater()
)需要很长时间才能执行。这样做会阻止棱镜渲染线程与FX Application线程同步,这意味着在runnable完成之前无法渲染下一帧。另一个是用太多的Runnable
来轰炸FX应用程序线程,这样队列的增长速度就快于消耗它们。 (基本上,由于队列总是有可用的东西,FX应用程序线程在这些情况下变成一个忙线程,并且无法保证棱镜渲染线程能够运行。)
因此,您问题的直接答案是Runnable
按照发布的顺序执行,就像它们可以在单个线程上一样。帧渲染(上限为60 fps)将暂时停止从队列中消耗Runnable
。
在伪代码中,我 猜测 (只是我对这是如何工作的直觉,这不是基于源代码)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();
}