2012-07-12 46 views
1

我可能在這裏錯過了一些東西,但我會嘗試解釋我想實現的目標,然後有人請告訴我我做錯了(我是:))並指出我在正確的方向?在Java FX UI中使用應該返回結果的線程

我正在使用JavaFX 2.0,但我認爲這個問題可能適用於Swing或任何UI框架。

我想爲我的應用程序開發一個簡單的啓動畫面,當啓動畫面啓動時,我想要有一個消息標籤,用於更新用戶發生的事情,關於配置後端應用程序。我的應用程序啓動有2個步驟,第一步使用Spring初始化應用程序上下文,該應用程序上下文又初始化數據庫(JPA 2.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; 
     } 
    } 
} 

任何人都可以指出我的錯誤?

回答

1

我建議使用Task來執行啓動任務和消息進度回到啓動屏幕(類似於爲此前創建的sample中的方法stackoverflow question on splash screens)。如果你想讓任務中的東西順序運行,只需使用一個線程來完成任務而不是兩個。您的任務

示例代碼可能看起來像:

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()); 
+0

乾杯@jewelsea我調查之前使用的任務,但再次我的問題是,當只需要1使用2線程,你指出這一點,以及我接受你的答案,由於JavaFX代碼。再次感謝。 – 2012-07-12 20:15:32

1

解決您的問題,您可以使用CountDownLatch 這可以用以下方式:

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

在一個側面說明:因爲你的任務是連續的,爲什麼需要有多個線程?線程用於運行多個並行運行的任務,並且您的案例不會爲線程創建用例,因爲這些操作是連續的

+0

「在一個側面說明:爲你的任務是連續的,爲什麼會出現需要一種具有多個線程?」這就是我出錯的地方......出於某種原因,我的頭腦中已經有兩個任務需要在不同的線程中運行,並且您的確切正確,我的用例並不要求它們處於單獨的線程中。我也不知道CountDownLatch類,所以我非常感謝你提供這些信息。 – 2012-07-12 20:13:17

+0

你更歡迎;) – GETah 2012-07-12 20:16:31

相關問題