1

我可能在这里遗漏了一些东西,但我会尝试解释我想要实现的目标,然后有人请告诉我我做错了(我是:))并指出我正确的方向?

我正在使用 JavaFX 2.0,但我认为这个问题适用于 Swing 或任何 UI 框架。

我想为我的应用程序开发一个简单的启动屏幕,当启动屏幕启动时,我想要一个消息标签,用于向用户更新正在发生的事情,关于配置应用程序的后端。我的应用程序启动有 2 个步骤,第一步使用 Spring 初始化应用程序上下文,进而初始化数据库(JPA 2.0/Hibernate/etc)。我的应用程序启动过程的第二部分将查询数据库以获取将用于填充 UI 的初始数据。这两个步骤都需要在我关闭初始屏幕之前完成,并且在每个步骤之间我想更新初始屏幕中的标签以让用户知道当时正在完成哪个阶段。

我已将其分解为以下使用 JavaFX 和按钮的简单程序,当按下按钮时,会创建一个新线程,该线程会启动另一个类,该类仅对 abitary 值执行一些计数,然后创建另一个线程模拟启动过程的第二步,但我的问题是第二个线程尝试在第一个线程完成之前运行,结果运行到 NPE。

下面是一些突出此问题的简单代码的细分:

public class Test extends Application
{
    private LongTester lt;
    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage)
    {
        Button btn = new Button();
        final Label lblText = new Label("Starting");

        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>()
        {
            @Override
            public void handle(ActionEvent event)
            {                
                new Thread(new ConstructorRunnable()).start();

                lblText.setText("More Loading?");

                new Thread(new MethodRunnable()).start();

                lblText.setText("Finished");
            }
        });

        HBox root = new HBox();
        root.getChildren().add(btn);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private class ConstructorRunnable implements Runnable
    {
        @Override
        public void run()
        {
            lt = new LongTester();
        }
    }

    private class MethodRunnable implements Runnable
    {
        @Override
        public void run()
        {
            lt.countAgain();
        }
    }

    private class LongTester
    {
        public LongTester()
        {
            for (int idx = 0; idx < 1000000; idx++)
            {
                System.out.println("Constructor: " + idx);
            }
        }

        public Boolean countAgain()
        {
            for (int idx = 0; idx < 1000000; idx++)
            {
                System.out.println("Method: " + idx);
            }
            return Boolean.TRUE;
        } 
    }
}

谁能指出我的错误?

4

2 回答 2

1

要解决您的问题,您可以使用 aCountDownLatch 这可以通过以下方式使用:

private class ConstructorRunnable implements Runnable {
   CountDownLatch gate_ = null;
   public ConstructorRunnable(CountDownLatch gate){
      gate_ = gate;
   } 
   @Override
   public void run() {
      lt = new LongTester();
      gate_.countDown();  // Signal the second thread to start
   }
}

private class MethodRunnable implements Runnable{
  CountDownLatch gate_ = null;
  public MethodRunnable(CountDownLatch gate){
      gate_ = gate;
  }
  @Override
  public void run(){
     CountDownLatch.await(); // Wait for the first thread to finish
     lt.countAgain();
  }
}

现在可以这样使用:

CountDownLatch gate = new CountDownLatch(1);
new Thread(new ConstructorRunnable(gate)).start();
lblText.setText("More Loading?");
new Thread(new MethodRunnable(gate)).start();
lblText.setText("Finished");

附带说明:由于您的任务是连续的,为什么需要多个线程?线程用于运行并行运行的多个任务,并且您的案例不会成为线程的用例,因为这些操作是顺序的

于 2012-07-12T19:47:20.370 回答
1

我建议使用任务来执行您的启动任务并将消息进度返回到您的初始屏幕(类似于此示例中为初始屏幕上的先前stackoverflow 问题创建的方法)。如果您希望任务中的内容按顺序运行,只需为任务使用一个线程而不是两个线程。

您的任务的示例代码可能类似于:

final Task<Data> initTask = new Task() {
  @Override protected Data call() throws InterruptedException {
    updateMessage("Initializing Application");
    MyApp.initializeAppContext(); 
    updateMessage("Loading Data");
    Data data = DB.loadData(); 
    updateMessage("Data Loaded");

    return data;
  }

showSplash(initStage, initTask);
new Thread(initTask).start();
showMainStage(initTask.valueProperty());
于 2012-07-12T20:00:50.767 回答