似乎已经普遍理解JavaFX UI线程的上限为每秒60个更新(12)。据我了解,更新意味着pulse


  脉冲是一个事件,它向JavaFX场景图指示它
  是时候同步场景图上元素的状态了
  与棱镜。最多以每秒60帧(fps)的速度限制脉冲
  并在场景图上运行动画时触发。甚至
  当动画不运行时,当有东西进入时会安排一个脉冲
  场景图已更改。例如,如果按钮的位置是
  更改后,将安排一个脉冲。


资料来源:https://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm

为了弄清楚例如对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堆叠在一起,并且UI线程穿插运行,然后执行所有排队的Runables
UI线程执行前60个调用,然后将Runable排队。


但是,在经过一些初始延迟之后,可运行对象排队,然后所有这些都由UI线程一次处理,结果是两个线程按顺序运行:


  任务1281运行于2016-01-25T18:37:00.269任务1281执行于
  Application Thread at 2016-01-25T18:37:00.269 Task 1282 at at
  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线程上推送的Runable会发生什么。是在UI线程的一次运行中按顺序执行了所有已排队的Runable,还是仅执行了一个Runable,其余必须等待?当连续在UI线程上推动超过该限制的Runable时,第二种情况将导致严重的问题。

最佳答案

脉冲上限为60fps。提交给FX Application Thread的Runnable不是。

据我了解,有两个线程:FX应用程序线程和棱镜渲染线程。 FX应用程序线程从队列中消费Runnable并执行它们。棱镜渲染线程每秒最多执行60次,与FX Application Thread同步(从而暂时阻止其执行新的可运行对象)并渲染框架,然后释放FX Application Thread。

因此,FX Application Thread将尽快执行您的Runnable,并且不限于每1/60秒运行一次。但在渲染帧期间不会从队列中获取新的可运行对象。 (用户事件在FX Application线程上的处理方式与您发布的Runnable相似。)

因此,有两种方法可以使不好的事情发生。一种是在FX Application Thread上执行代码(无论是在事件处理程序中还是在发布到RunnablePlatform.runLater()中),这些代码都需要花费很长时间来执行。这样做将阻止棱镜渲染线程与FX Application线程同步,这意味着在可运行对象完成之前无法渲染下一帧。另一个是用太多的Runnable轰击FX Application Thread,以使队列的增长快于其消耗的速度。 (本质上,由于队列中总是有可用的东西,因此在这种情况下,FX应用程序线程会成为繁忙的线程,并且无法保证棱镜渲染线程能够运行。)

因此,直接回答您的问题是Runnable会按照它们发布的顺序执行,并且要尽快在单个线程上执行。帧渲染的上限为60 fps,将暂时停止消耗队列中的Runnable

用伪代码,我猜(只是我的直觉,这不基于源代码),FX Application Thread看起来像

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();
}

关于java - JavaFX FPS上限为60 FPS,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34999121/

10-11 01:31
查看更多