在JavaFX中运行线程

时间:2015-03-30 23:26:26

标签: java multithreading 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);
}

当我运行它时,“Hello”连续打印,但当我尝试关闭它时,线程继续运行,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

我查看了我的代码,但找不到任何null的东西。

2 个答案:

答案 0 :(得分:1)

假设Things是您展示的课程的名称,您并未停止正确的主题。

当你致电launch(args)时,FX Toolkit会创建一个应用程序类的实例(我假设它是Things),创建一个Stage,并传递{ {1}}到应用程序实例的Stage方法(在FX应用程序线程上执行该方法)。

因此,您创建一个实例,并在该实例上启动该线程。然后FX工具包创建第二个实例,并且在该实例上您试图阻止该线程。所以你要停止与你开始的线程不同的线程。

解决此问题的方法是删除行

start(...)

来自Things things = new Things(); things.startThread(); 方法,只需添加

main(...)

this.startThread(); 方法的开头。

此外,正如@RyanJ指出的那样,当你有一个线程等待第二个线程完成时,你就会死锁,并且两者都在尝试执行相同的start(...)方法。此外,您应该将synchronized声明为running,因为它是从多个线程访问的:

volatile

这样可行:

private volatile boolean running ;

最后一点:如果这是您打开的唯一窗口,只要没有非守护程序线程在运行,JVM就会在您关闭窗口时退出。所以你可以让JVM通过使它成为一个守护程序线程来杀死你的线程。如果这适用于您的真实应用,您可以通过覆盖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); } } 方法进行所需的任何清理:

stop

答案 1 :(得分:0)

你可能会遇到活锁/死锁情况。

在run方法中,while循环终止后,你的线程会尝试自行停止,然后尝试等待自己死掉。这是一个同步方法,意味着一次只能运行一个线程。

当您从main方法调用它并尝试停止该线程时,它将尝试加入该线程并等待它死亡。由于第一次调用已经在执行stopThread方法,当while循环在run方法中终止时,它将停留在那里并阻塞,不会导致线程死亡。由于线程没有死亡,对stopThread的第一次调用将无法完成。

您可以从stopThread方法移除run,它应该会表现得更好,但是根据您在此处发布的内容,看起来更好的设计可能是有序的。如果我的建议有效,我会很好奇。

进一步检查后,您的线程中的静态/非静态数据也会出现问题。

我假设您的Things类实现了Runnable,因此当您将其传递给Thread构造函数并启动它时,它会获取其自己的副本堆栈,它是自己的局部变量。当您在stopThread方法中调用close时,您没有设置正确的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;
    }
}

在单独的Test / Application类中,您可以操纵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
    }
}