JavaFX - 如何在退出应用程序之前关闭所有正在运行的线程?

4
我使用JavaFX创建了一个客户端/服务器聊天应用程序。当按下“启动服务器”按钮时,我的ServerController将创建一个运行在单独线程上的Server对象。
这里是我的问题:
每当我只是用默认的“x”按钮关闭窗口时,服务器线程就会终止,但不会通知活动客户端服务器已关闭。我需要像使用“停止服务器”按钮一样向客户端通知服务器已关闭。我的服务器保存有控制器对象的引用。
public class ServerController {

@FXML
TextArea chatEvent_txt;
@FXML
Button start_btn;
@FXML
Button stop_btn;
private Server server;
private boolean started = false;
private StringBuffer chatEvents = new StringBuffer("");

/**
 * @param e
 */
@FXML
public void startButtonAction(ActionEvent e) {
    if (!started) {
        this.server = new Server(this);
        new Thread(server).start();
        started = true;
        chatEvents = chatEvents.append("Server started.." + "\n");
        chatEvent_txt.setText(chatEvents.toString());
    } else {
        chatEvent_txt.setText("Server already started\n");
    }
}

这是我的“停止服务器”按钮,它通知所有客户端服务器将关闭,然后关闭所有套接字以及相关内容。
@FXML
public void stopButtonAction(ActionEvent e) {
    if (started) {
        server.setRunning(false);
        chatEvent_txt.setText("Server wurde gestoppt\n");
        started = false;
    }
}

方法setRunning()只会将一个变量设置为false,使得线程离开接受新客户端的while循环。当while循环结束后,服务器将向所有客户端发送一条消息,以便他们知道服务器将被关闭。

我的服务器上的run方法:

@Override
public void run() {
    try {
        serverSocket.setSoTimeout(500);
        while (running) {
            try {
                clientSocket = serverSocket.accept();
                new Thread(new Listener(clientSocket, this)).start();
            } catch (IOException e) {
            }
        }
    } catch (IOException e1) {
        e1.printStackTrace();
    }

    try {
        broadcastMessage(new Message(Message.Type.DISCONNECT_OK, "Server"));
        serverSocket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

我应该如何让“x”按钮与我的“停止服务器”按钮执行同样的操作?

2
你应该实现 onCloseRequest 处理程序,并在此处理程序中优雅地关闭你的服务器。 - Maxim
虽然给出的答案可行,但我更喜欢使用shutdown-hook,这样即使使用Ctrl+C停止程序也会被调用。 - MalaKa
1
@MalaKa 很难正确地获取与FX Toolkit交互的关闭挂钩(甚至不知道正确的实现方式是什么)。事实上,文档明确指出:“尝试使用其他基于线程的服务,如AWT事件分派线程,可能会导致死锁。” - James_D
@GurV,您链接的问题是关于退出运行中的线程,而不是拦截窗口中的关闭按钮。我不明白这与那个问题重复了。 - James_D
1个回答

2

将其重构为

@FXML
public void stopButtonAction(ActionEvent e) {
    shutdown();
}

public void shutdown() {
    if (started) {
        server.setRunning(false);
        chatEvent_txt.setText("Server wurde gestoppt\n");
        started = false;
    }
}

然后当您加载和显示FXML时,您可以执行以下操作:

FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
someStage.setScene(scene);

ServerController controller = loader.getController();
someStage.setOnCloseRequest(e -> controller.shutdown());

如果您需要在按下关闭按钮时窗口不关闭(例如,如果您需要在编程方式关闭窗口之前等待所有内容优雅地关闭),则可以在 onCloseRequest 处理程序中调用 e.consume()


谢谢,这个方法可行。我通过覆盖应用程序类中的stop()方法来解决了这个问题。不过我不确定这是否是正确的方式。 - user7313623

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接