了解 JavaFX WebView 线程模型

Moe*_*oeb 5 java multithreading javafx

我试图了解 JavaFX 线程是如何工作的。这里的描述没有多大帮助。

例如,在下面的代码,印刷的顺序总是A然后B然后Z,这表明,无论是load()呼叫和里面的代码changed()都在同一线程上运行。

但是我不明白,为什么线程不会在最后一个之后退出Thread.sleep(2000)(因为没有更多的工作要做)?

我希望里面的代码changed()每次都在一个新线程上运行,并且完全不知道这是如何工作的!

public class Test extends Application {

  @Override
  public void start(Stage stage) throws Exception {

    final WebView webView = new WebView();
    Scene scene = new Scene(webView);
    stage.setScene(scene);
    stage.show();

    webView.getEngine().getLoadWorker().stateProperty()
        .addListener(new ChangeListener<State>() {
      @Override
      public void changed(ObservableValue<? extends State> ov, State t, State t1) {
        if (t1 == Worker.State.SUCCEEDED) {
          System.out.println("Z"); // <---
        }
      }
    });

    webView.getEngine().load("http://www.google.com");

    System.out.println("A"); // <---
    Thread.sleep(2000);
    System.out.println("B"); // <---
    Thread.sleep(2000);
  }

  public static void main(String[] args) {
    launch(args);
  }
}
Run Code Online (Sandbox Code Playgroud)

Nik*_*los 4

简短回答

start()changed()从“JavaFX应用程序线程”运行。该线程负责监视 UI 中的事件,将它们分派给用户代码并重新绘制 UI。与退出时不同Runnable.run(),线程终止,start()由“JavaFX 应用程序线程”以类似循环的方式调用:当start()结束时,执行下一次迭代。(实际上迭代是异步运行/等待事件,以免消耗所有 CPU 资源,但作为简化模型,循环是可以的。)

更长的答案

我会尝试更详细地解释一下。

将打印语句放置为(未缩进以突出):

    public void start(Stage stage) throws Exception {
System.out.println("2: " + Thread.currentThread().getName());
        ...
        webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
                    @Override
                    public void changed(ObservableValue<? extends State> ov, State t, State t1) {
                        if (t1 == Worker.State.SUCCEEDED) {
System.out.println("3: " + Thread.currentThread().getName());
                            System.out.println("Z"); // <---
                        }
                    }
                });

        webView.getEngine().load("...");

        System.out.println("A"); // <---
        Thread.sleep(2000);
        System.out.println("B"); // <---
        Thread.sleep(2000);
    }

    public static void main(String[] args) {
System.out.println("1: " + Thread.currentThread().getName());
        launch(args);
System.out.println("4: " + Thread.currentThread().getName());
    }
Run Code Online (Sandbox Code Playgroud)

观察控制台和应用程序:

用户操作:启动应用程序
控制台:立即显示以下内容:

    public void start(Stage stage) throws Exception {
System.out.println("2: " + Thread.currentThread().getName());
        ...
        webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
                    @Override
                    public void changed(ObservableValue<? extends State> ov, State t, State t1) {
                        if (t1 == Worker.State.SUCCEEDED) {
System.out.println("3: " + Thread.currentThread().getName());
                            System.out.println("Z"); // <---
                        }
                    }
                });

        webView.getEngine().load("...");

        System.out.println("A"); // <---
        Thread.sleep(2000);
        System.out.println("B"); // <---
        Thread.sleep(2000);
    }

    public static void main(String[] args) {
System.out.println("1: " + Thread.currentThread().getName());
        launch(args);
System.out.println("4: " + Thread.currentThread().getName());
    }
Run Code Online (Sandbox Code Playgroud)

应用程序:出现主窗口...但是没有内容!为什么?因为负责绘制 UI 的“JavaFX 应用程序线程”sleep()在点“A”之前是 -ing。

控制台:2秒后打印“A”
应用程序:仍在休眠“B”,窗口中仍然没有内容

控制台:2秒后打印“B”
应用程序:事物变得栩栩如生,内容开始填充;UI 线程不再被阻塞,sleep()因此它可以自由地执行其工作。

请注意,尽管事实start()已经结束,launch()不是,因此主线程仍然处于活动状态并且运行良好,因此应用程序处于活动状态。过了一会儿,页面加载完毕:

安慰

1: main
2: JavaFX Application Thread
Run Code Online (Sandbox Code Playgroud)

用户操作:按关闭
控制台

3: JavaFX Application Thread
Z
Run Code Online (Sandbox Code Playgroud)

应用程序:窗口关闭,应用程序退出

为了更深入地了解本主题,您可以在main()和中放置断点start()并观察各个活动的线程。

评论:从 UI 线程中的调用中观察到的行为sleep()就是为什么如果您希望应用程序具有响应能力,则决不能在 UI 线程内执行长时间运行的任务。