在JavaFXUI中使用应返回结果的线程

在JavaFXUI中使用应返回结果的线程,java,swing,user-interface,thread-safety,javafx-2,Java,Swing,User Interface,Thread Safety,Javafx 2,我可能在这里遗漏了一些东西,但我会尝试解释我想要实现的目标,然后有人告诉我我做错了(我是:),并为我指出正确的方向 我使用的是JavaFX2.0,但我认为这个问题将适用于Swing或任何UI框架 我想为我的应用程序开发一个简单的启动屏幕,当启动屏幕启动时,我想有一个消息标签,用于更新用户在配置应用程序后端时发生的事情。我的应用程序启动有两个步骤,第一步使用Spring初始化应用程序上下文,然后初始化DB(JPA2.0/Hibernate/etc)。我的应用程序启动过程的第二部分将向数据库查询用于

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

我使用的是JavaFX2.0,但我认为这个问题将适用于Swing或任何UI框架

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

我将其分解为以下使用JavaFX和按钮的简单程序,当按下按钮时,将创建一个新线程,启动另一个类,该类只对一个特定值执行一些计数,然后创建另一个线程来模拟启动过程的第二步,但我的问题是,第二个线程试图在第一个线程完成之前运行,结果运行到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;
        } 
    }
}
公共类测试扩展应用程序
{
私人Longtest lt;
公共静态void main(字符串[]args)
{
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage)
{
按钮btn=新按钮();
最终标签lblText=新标签(“开始”);
btn.setText(“说‘你好,世界’”);
btn.setOnAction(新的EventHandler()
{
@凌驾
公共无效句柄(ActionEvent事件)
{                
新线程(新构造函数未知()).start();
lblText.setText(“更多加载?”);
新线程(新方法runnable()).start();
lblText.setText(“已完成”);
}
});
HBox根=新的HBox();
root.getChildren().add(btn);
场景=新场景(根,300,250);
setTitle(“你好,世界!”);
初级阶段。场景(场景);
primaryStage.show();
}
私有类构造函数Runnable实现可运行
{
@凌驾
公开募捐
{
lt=新Longtest();
}
}
私有类MethodRunnable实现Runnable
{
@凌驾
公开募捐
{
中尉再次倒数();
}
}
私人班龙特酒店
{
公共部门
{
对于(intidx=0;idx<1000000;idx++)
{
System.out.println(“构造函数:+idx”);
}
}
公共布尔计数()
{
对于(intidx=0;idx<1000000;idx++)
{
System.out.println(“方法:+idx”);
}
返回Boolean.TRUE;
} 
}
}

有人能指出我的错误吗?

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

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");

另一方面:由于任务是连续的,为什么需要有多个线程?线程被设置为并行运行多个任务,而您的案例并不构成线程的用例,因为这些操作是连续的

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

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

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());
final Task initTask=新任务(){
@Override protected Data call()引发InterruptedException{
更新消息(“初始化应用程序”);
MyApp.initializeAppContext();
更新消息(“加载数据”);
Data Data=DB.loadData();
更新消息(“加载的数据”);
返回数据;
}
showSplash(initStage、initTask);
新线程(initTask.start();
showMainStage(initTask.valueProperty());

“另一方面,由于任务是连续的,为什么需要多个线程?”这就是我出错的地方…出于某种原因,我在脑海中一直认为这两个任务都需要在不同的线程中运行,而你完全正确,我的用例不要求它们在不同的线程中运行。我也不知道倒计时闩锁类,所以我感谢你提供的这一点信息。干杯@jewelsea我查看了using任务之前,但我的问题是,我使用了2个线程,而只需要1个线程,您也指出了这一点,由于JavaFX代码,我接受您的答案。再次感谢。