如何等待JFrame线程从JavaFX应用程序内部完成?

时间:2018-06-10 21:49:36

标签: java multithreading macos swing javafx

我正在尝试从JavaFX应用程序内部创建一个JFrame窗口。我正在使用Oracle Java 8,我能够在Linux,Windows中完成我需要的工作,但不能在Mac OS中完成。这似乎是一个JVM实现问题。以下是我的要求:

  • 我需要调用一个库方法来创建一个游戏窗口(来自JFrame的扩展类)。我需要能够在游戏中使用键盘。
  • 我可以访问库的源代码,但我无意更改库的源代码
  • 我需要在游戏结束后处理游戏信息,所以我的主要线程需要等到游戏结束

我在下面创建了示例应用程序。应用程序在Linux和Windows下工作正常,但在Mac OS中,当我尝试等待JFrame线程完成应用程序冻结并且未显示JFrame时。 SwingUtilities.invokeLater不会冻结,但它不会等待JFrame线程完成。因为我需要等待JFrame线程,invokeLater对我来说不是一个选项。

JFrameCreator.java

public class JFrameCreator implements Runnable {
    @Override
    public void run() {
        System.out.println("Creating JFrame");
        JFrame frame=new JFrame();
        frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        frame.setSize(300,300);
        frame.setVisible(true);
        for (int i = 0; i < 100; i++) {
            frame.getContentPane().getGraphics().drawOval(150-i/2,150-i/2,i,i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

sample.fxml

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="100.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Button layoutX="51.0" layoutY="10.0" mnemonicParsing="false" onAction="#createJFrame" text="New JFrame" />
      <RadioButton fx:id="cbThread" layoutX="10.0" layoutY="40.0" mnemonicParsing="false" selected="true" text="Thread">
         <toggleGroup>
            <ToggleGroup fx:id="tg" />
         </toggleGroup>
      </RadioButton>
      <RadioButton fx:id="cbInvokeWait" layoutX="10.0" layoutY="60.0" mnemonicParsing="false" text="invokeAndWait" toggleGroup="$tg" />
      <RadioButton fx:id="cbInvokeLater" layoutX="10.0" layoutY="80.0" mnemonicParsing="false" text="invokeLater" toggleGroup="$tg" />
   </children>
</AnchorPane>

Controller.java

public class Controller {
    @FXML RadioButton cbThread;
    @FXML RadioButton cbInvokeWait;
    @FXML RadioButton cbInvokeLater;

    public void createJFrame(javafx.event.ActionEvent actionEvent) {
        Runnable r=new JFrameCreator();
        try {
            if (cbThread.isSelected()) {
                Thread t=new Thread(new JFrameCreator());
                t.start();
                t.join();
            }
            else if(cbInvokeWait.isSelected())
                SwingUtilities.invokeAndWait(r);
            else
                SwingUtilities.invokeLater(r);
        }
         catch (InterruptedException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Main.java

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Swing in JavaFX");
        primaryStage.setScene(new Scene(root, 200, 100));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

我还尝试了一个使用Thread.isAlive()方法的while循环,但它的行为也类似于Thread.join()。我怎样才能达到我的要求?

1 个答案:

答案 0 :(得分:1)

您可以使用其中一个同步类,例如CountDownLatch。将其传递给<html class=""><head> <meta charset="UTF-8"> <title> Zenith</title> <link rel="stylesheet" href="assets/css/style.css"> <style id="__web-inspector-hide-shortcut-style__" type="text/css"> .__web-inspector-hide-shortcut__, .__web-inspector-hide-shortcut__ *, .__web-inspector-hidebefore-shortcut__::before, .__web-inspector-hideafter-shortcut__::after { visibility: hidden !important; } </style></head> <body> <div class="wrapper"> <h1 class="glitch">Zenith</h1> </div> <a class="glitch-btn" href="http://steamcommunity.com/id/1zv" target="_blank"> <div class="label">Steam</div> <div class="mask"><span>Steam</span></div> <div class="mask"><span>Steam</span></div> <div class="mask"><span>Steam</span></div> <div class="mask"><span>Steam</span></div> <div class="mask"><span>Steam</span></div></a> <a class="glitch-btn1" href="https://www.youtube.com/channel/UCwBh4mBv-QrIynWxCj-8AVw" target="_blank"> <div class="label">Youtube</div> <div class="mask"><span>Youtube</span></div> <div class="mask"><span>Youtube</span></div> <div class="mask"><span>Youtube</span></div> <div class="mask"><span>Youtube</span></div> <div class="mask"><span>Youtube</span></div></a> <script src="assets/javascript/script.js"></script> </body></html>并在Runnable方法结束时致电latch.countDown()。同时,您的JavaFX线程将在run()暂停。但是,这会冻结JavaFX UI,因此这可能不是您想要的。

另一种选择是使用回调。类似的东西:

latch.await()

第二个选项不会导致JavaFX线程等待但它确实允许您在适当的时候做出反应。您可以将public class JFrameCreator implements Runnable { private final Runnable onFinished; public JFrameCreator(Runnable onFinished) { this.onFinished = onFinished; } @Override public void run() { // do your work... Platform.runLater(onFinished); // to communicate to JavaFX Application // Thread that JFrameCreator is complete } } 的类型更改为您需要的任何内容;例如onFinished如果你想传递回调一些对象。