2015-03-30 130 views
0

我正在用JavaFX製作一個簡單的按鈕遊戲,我需要一個線程來運行,以便我可以檢查一個敵人或玩家是否還活着。我的問題不是開始和使用線程,而是在窗口關閉時停止它。在JavaFX中運行線程

我做了一個樣本類來演示我的意思。

private Thread thread; 
private boolean running; 
private Stage window; 

public void run() { 
    while (running) { 
     System.out.println("Hello"); 
    } 
    stopThread(); 
} 

public synchronized void startThread() { 
    running = true; 
    thread = new Thread(this, "Monitor"); 
    thread.start(); 
} 

public synchronized void stopThread() { 
    running = false; 
    try { 
     thread.join(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

public void start(Stage stage) throws Exception { 
    window = new Stage(); 
    window = stage; 
    Pane layout = new Pane(); 
    Scene scene = new Scene(layout); 

    window.setOnCloseRequest(e -> { 
     e.consume(); 
     close(); 
    }); 
    window.setScene(scene); 
    window.show(); 
} 

public void close() { 
    window.close(); 
    stopThread(); 
} 

public static void main(String[] args) { 
    Things things = new Things(); 
    things.startThread(); 
    launch(args); 
} 

當我運行它,不斷地「你好」打印,但是當我嘗試關閉它,線程繼續運行和Eclispe的進入調試模式說:

Thread [JavaFX Application Thread] (Suspended (exception NullPointerException)) QuantumToolkit.runWithoutRenderLock(Supplier<T>) line: not available GlassWindowEventHandler.handleWindowEvent(Window, long, int) line: not available WinWindow(Window).handleWindowEvent(long, int) line: not available WinWindow(Window).notifyClose() line: not available WinApplication._runLoop(Runnable) line: not available [native method] WinApplication.lambda$null$145(Runnable) line: not available 2091156596.run() line: not available Thread.run() line: not available

我已經看過但是,在我的代碼周圍,找不到任何空的內容。

+0

你退出應用程序時關閉該窗口,還是有其他的展示窗口? – 2015-03-30 23:31:11

回答

0

您可能正在遇到活鎖/死鎖情況。

在你的run方法中,在while循環終止之後,你的線程試圖停止自己,然後試圖等待自己死掉。這是一個同步方法,意味着一次只能有一個線程運行它。

當您從主方法調用它並嘗試停止線程時,它會嘗試加入線程並等待它死亡。由於這個第一個調用已經在執行你的stopThread方法,所以當while循環在你的run方法中終止時,它將會停在那裏並阻塞,不會導致線程死亡。由於線程不會死亡,因此第一次調用stopThread將不會完成。

您可以從您的run方法中刪除stopThread,它應該表現得更好,不過從您在這裏發佈的內容看,它看起來好像是一個更好的設計。如果我的建議有效,我會很好奇。

經過進一步檢查,您的線程中的靜態/非靜態數據也存在問題。

我假設你Things類實現Runnable,因此,當你把它傳遞給Thread構造並啓動它,它就會堆棧其自己的副本,以及它自己的局部變量。當你在你的close方法中調用stopThread時,你沒有設置正確的running變量(它不是線程運行的那個)。如果您將該字段設置爲static並刪除thread.join...代碼,則它的行爲正確。

編輯: 作爲一個更好的設計選擇,利用Threads/Runnables獲得的功能以及它們通常在應用程序中管理的方式可能更爲明智(更多信息,因爲您使用的是JFX,所以應該看看服務/任務)

拆分線程實現和主類,並利用線程本身的功能來阻止它。

就拿這個例子中,這是顯著更清潔,和作品一樣好:

public class Things implements Runnable { 
    private volatile boolean running; // this indicates it's running 

    public Things() { 
     running = true; // thread is running when it's created (though not really until "run" is called) 
    } 

    // called from start() 
    public void run() { 
     while (running) { 
      System.out.println("Hello"); 
     } 
    } 

    // call this method to stop the thread, from outside on the object reference 
    public synchronized void stop() { 
     running = false; 
    } 
} 

在一個單獨的測試/應用類,你可以操縱Things參考做你想要什麼,不保持基準到實際的線程。

public class Test extends Application { 
    private Stage window; 
    private static Things things; 

    public void start(Stage stage) throws Exception { 
     window = new Stage(); 
     window = stage; 
     Pane layout = new Pane(); 
     Scene scene = new Scene(layout); 

     window.setOnCloseRequest(new EventHandler<WindowEvent>() { 
      @Override 
      public void handle(WindowEvent windowEvent) { 
       close(); 
      } 
     }); 
     window.setScene(scene); 
     window.show(); 
    } 


    // on close 
    public void close() { 
     window.close(); 
     things.stop(); // stop the things object directly 
    } 

    public static void main(String[] args) { 
     things = new Things(); // create new runnable 
     Thread t = new Thread(things,"Monitor"); // create thread 
     t.start(); // start thread 
     launch(args); // launch app 
    } 
} 
+0

與您的更新交叉發佈;並不意味着要竊取你的答案... – 2015-03-30 23:50:43

+0

非常感謝,它的工作! – 2015-03-30 23:52:07

+0

@James_D不用擔心,當你發佈的時候我正在研究一個建議的設計,OP的信息越多越好。 – 2015-03-30 23:53:58

1

假設Things是你教,你不停止正確的線程類的名稱。

當你調用launch(args),外匯工具包創建應用程序類的一個實例(我假設是Things),創建一個Stage,並傳遞Stage到應用程序實例start(...)方法(執行該方法的FX應用程序線程)。

因此您創建一個實例,並在該實例上啓動該線程。然後FX工具包創建第二個實例,並且在那個實例上,您試圖停止該線程。所以你正在停止與你開始的線程不同的線程。

此解決方法是從你的main(...)方法去除線

Things things = new Things(); 
things.startThread(); 

,只是添加

this.startThread(); 

start(...)方法的開始。

此外,正如@RyanJ指出的那樣,由於您有一個線程正等待第二個線程完成,並且兩個都試圖執行相同的​​方法,所以會發生死鎖。此外,你應該申報runningvolatile,因爲它是從多個線程訪問:

private volatile boolean running ; 

所以此工程:

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.layout.Pane; 
import javafx.stage.Stage; 


public class Things extends Application implements Runnable { 

    private Thread thread; 
    private volatile boolean running; 
    private Stage window; 

    @Override 
    public void run() { 
     while (running) { 
      System.out.println("Hello"); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
//  stopThread(); 
     running = false ; 
    } 

    public synchronized void startThread() { 
     running = true; 
     thread = new Thread(this, "Monitor"); 
     thread.start(); 
    } 

    public synchronized void stopThread() { 
     running = false; 
     try { 
      thread.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    @Override 
    public void start(Stage stage) throws Exception { 
     startThread(); 
     window = stage; 
     Pane layout = new Pane(); 
     Scene scene = new Scene(layout); 

     window.setOnCloseRequest(e -> { 
      e.consume(); 
      close(); 
     }); 
     window.setScene(scene); 
     window.show(); 
    } 

    public void close() { 
     window.close(); 
     stopThread(); 
    } 

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

最後一點:如果這是唯一的窗口已打開,該只要沒有非守護程序線程正在運行,JVM將在您關閉該窗口時退出。所以你可以讓JVM通過使它成爲守護線程來殺死你的線程。如果這種方式更適合您的真正的應用程序,你可以做你需要通過重寫stop方法的任何清理:

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.layout.Pane; 
import javafx.stage.Stage; 


public class Things extends Application implements Runnable { 

    private Thread thread; 
    private volatile boolean running; 
    private Stage window; 

    @Override 
    public void run() { 
     while (running) { 
      System.out.println("Hello"); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
//  stopThread(); 
     running = false ; 
    } 

    public synchronized void startThread() { 
     running = true; 
     thread = new Thread(this, "Monitor"); 

     thread.setDaemon(true); 

     thread.start(); 
    } 


    @Override 
    public void start(Stage stage) throws Exception { 
     startThread(); 
     window = stage; 
     Pane layout = new Pane(); 
     Scene scene = new Scene(layout); 

     window.setScene(scene); 
     window.show(); 
    } 



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

謝謝你的快速回答。 – 2015-03-30 23:54:00