首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaFX 視窗關閉事件監聽與多執行緒優雅關閉實戰

     編輯:Java 相關知識

問題背景:JavaFX 多執行緒關閉陷阱

在 JavaFX 應用程式開發中,當主視窗關閉時,預設情況下 JVM 不會自動終止仍在執行的背景執行緒。這會導致程式看似已關閉,實際上進程仍殘留在記憶體中繼續消耗資源。特別是當背景執行緒持有資料庫連線、檔案控制代碼或網路連接時,更會造成資源洩漏與資料不一致風險。

解決方案:監聽視窗關閉事件

JavaFX 提供 setOnCloseRequest() 方法,允許開發者在視窗關閉前插入自定義邏輯,例如通知背景執行緒安全停止、釋放資源、儲存狀態等。

完整實作範例

package sample;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class Main extends Application {
    // 模擬背景執行緒
    private BackgroundWorker backgroundWorker = new BackgroundWorker();

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("JavaFX 關閉事件示範");
        primaryStage.setScene(new Scene(root, 400, 300));
        primaryStage.show();

        // 核心:註冊視窗關閉事件監聽器
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                System.out.println("偵測到視窗關閉請求,開始優雅關閉流程...");
                
                // 1. 通知背景執行緒停止
                backgroundWorker.shutdown();
                
                // 2. 等待執行緒實際結束(可設定超時)
                try {
                    backgroundWorker.awaitTermination(3, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                
                System.out.println("背景執行緒已安全停止,程式即將退出");
            }
        });
    }

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

// 模擬背景工作執行緒
class BackgroundWorker {
    private volatile boolean running = true;
    private Thread workerThread;

    public BackgroundWorker() {
        workerThread = new Thread(() -> {
            while (running) {
                try {
                    System.out.println("背景執行緒執行中... " + new Date());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            System.out.println("背景執行緒已退出迴圈");
        });
        workerThread.start();
    }

    public void shutdown() {
        running = false;
        workerThread.interrupt();
    }

    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        workerThread.join(unit.toMillis(timeout));
        return !workerThread.isAlive();
    }
}

關鍵程式碼解析

// 核心監聽器註冊
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
    @Override
    public void handle(WindowEvent event) {
        System.out.print("監聽到視窗關閉");
        // 在此處執行清理邏輯
    }
});

執行效果驗證

當使用者點擊視窗右上角關閉按鈕時,控制台輸出:

偵測到視窗關閉請求,開始優雅關閉流程...
背景執行緒已安全停止,程式即將退出

控制台輸出截圖

進階應用場景

  • 資料庫連線池關閉:dataSource.close() 釋放所有連線
  • 定時任務停止:scheduler.shutdown() 停止排程器
  • WebSocket 連線斷開:發送關閉幀並等待確認
  • 暫存資料持久化:將記憶體中未儲存資料寫入磁碟
  • 日誌刷新:LogManager.shutdown() 確保日誌完整寫入

最佳實踐建議

  1. 使用 volatile 旗標:確保執行緒間可見性,避免編譯器最佳化導致旗標更新不被察覺
  2. 設定關閉超時:避免背景執行緒卡死導致程式無法退出
  3. 區分 Daemon 與 User 執行緒:非關鍵背景任務可設為 Daemon 執行緒,但關鍵資源釋放邏輯必須在 User 執行緒完成
  4. 整合 Spring/JavaFX 整合框架:如使用 @PreDestroy 或 DisposableBean 統一管理生命週期

正確處理視窗關閉事件是開發穩健 JavaFX 桌面應用的基本功,能有效避免「關不掉的程式」與資源洩漏問題,提升使用者體驗與系統穩定度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved