【问题标题】:JavaFX FPS cap at 60 FPSJavaFX FPS 上限为 60 FPS
【发布时间】:2016-01-25 17:59:02
【问题描述】:

似乎有一个共识,即 JavaFX UI 线程的上限为每秒 60 次更新(12)。我的理解是更新意味着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)));
        }
    }
}

我的期望是:

  • Runnables 堆叠出来,UI 线程穿插运行,然后执行所有排队的Runables
  • UI 线程执行前 60 个调用,然后进一步排队 Runables。

然而,在 Runables 排队并随后由 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 线程上推送Runables 会发生什么。是所有排队的Runables 在一次运行UI 线程中按顺序执行,还是只执行了一个Runable,其余的必须等待?第二种情况会导致严重的问题,即在 UI 线程上持续推送超过该限制的 Runables。

【问题讨论】:

    标签: java multithreading javafx


    【解决方案1】:

    脉冲的上限为 60fps。 Runnables 提交到 FX 应用程序线程不是。

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

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

    所以有几种方法可以让坏事发生。一种是在 FX 应用程序线程(无论是在事件处理程序中还是在Runnable 中发布到Platform.runLater())上的代码需要很长时间才能执行。这样做会阻止 prism 渲染线程与 FX Application 线程同步,这意味着在可运行对象完成之前无法渲染下一帧。另一种是用太多的Runnables 轰炸FX 应用程序线程,这样队列的增长速度就超过了它们可以被消耗的速度。 (本质上,由于队列总是有可用的东西,因此 FX 应用程序线程在这些情况下会变成一个繁忙的线程,并且不能保证 prism 渲染线程会一直运行。)

    因此,您的问题的直接答案是Runnables 按照它们发布的顺序执行,尽可能快地在单个线程上执行。帧渲染的上限为 60 fps,将暂时停止使用队列中的 Runnables。

    在伪代码中,我猜测(只是我的直觉,这不是基于源代码)FX 应用程序线程看起来像 p>

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

    【讨论】:

      猜你喜欢
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-16
      • 2018-10-28
      • 2022-12-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多