You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让ScheduledExecutorService在用户退出程序时正确终止?

问题解决思路

一、修复Alert弹窗一闪而过的问题

你当前使用的alert.show()是异步显示弹窗,不会阻塞后续代码执行,导致程序直接走到super.stop(),弹窗还没等用户操作就被关闭了。把它换成showAndWait()就能阻塞直到用户做出选择,同时还要处理用户选择“NO”时的应用关闭拦截:

@Override 
public void stop() throws Exception {
    Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Exit ?", ButtonType.YES, ButtonType.NO);
    // 用showAndWait()替代show(),阻塞等待用户选择
    Optional<ButtonType> result = alert.showAndWait();
    
    if (result.isPresent() && result.get() == ButtonType.YES) {
        BlueSlime.scheduler.shutdown();
        // 可选:等待线程池终止,避免残留后台线程
        if (!BlueSlime.scheduler.awaitTermination(3, TimeUnit.SECONDS)) {
            BlueSlime.scheduler.shutdownNow();
        }
        super.stop();
    } else {
        System.out.println("No");
        // 阻止应用默认关闭流程
        Platform.setImplicitExit(false);
    }
}

二、优化ScheduledExecutorService的管理方式

用静态变量直接引用BlueSlime.scheduler会造成类之间强耦合,不利于维护,推荐两种更优的方案:

方式1:Main类持有线程池引用,传递给BlueSlime

把线程池的创建移到Main类中,通过构造方法或setter传递给BlueSlime,解耦类之间的依赖:

public class Main extends Application {
    private ScheduledExecutorService scheduler;

    @Override
    public void start(Stage primaryStage) {
        scheduler = Executors.newScheduledThreadPool(1);
        BlueSlime blueSlime = new BlueSlime(scheduler);
        // ... 其他初始化逻辑
    }

    @Override 
    public void stop() throws Exception {
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Exit ?", ButtonType.YES, ButtonType.NO);
        Optional<ButtonType> result = alert.showAndWait();
        
        if (result.isPresent() && result.get() == ButtonType.YES) {
            scheduler.shutdown();
            if (!scheduler.awaitTermination(3, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
            super.stop();
        } else {
            System.out.println("No");
            Platform.setImplicitExit(false);
        }
    }
}

// BlueSlime类
public class BlueSlime {
    private ScheduledExecutorService scheduler;
    private Runnable shootRunnable;

    public BlueSlime(ScheduledExecutorService scheduler) {
        this.scheduler = scheduler;
        this.shootRunnable = () -> System.out.println("SHOOT");
        scheduler.scheduleAtFixedRate(shootRunnable, 2, 2, TimeUnit.SECONDS);
    }

    // Enemy销毁时调用
    public void destroy() {
        scheduler.shutdown();
    }
}

方式2:使用JavaFX内置的ScheduledService(JavaFX 11+)

如果你的项目使用JavaFX 11及以上版本,推荐用ScheduledService替代ScheduledExecutorService,它天然和JavaFX生命周期绑定,无需手动管理线程池:

// 自定义定时任务服务
public class ShootService extends ScheduledService<Void> {
    @Override
    protected Task<Void> createTask() {
        return new Task<>() {
            @Override
            protected Void call() {
                System.out.println("SHOOT");
                return null;
            }
        };
    }
}

// BlueSlime类中使用
public class BlueSlime {
    private ShootService shootService;

    public BlueSlime() {
        shootService = new ShootService();
        shootService.setPeriod(Duration.seconds(2));
        shootService.start();
    }

    public void destroy() {
        shootService.cancel();
    }
}

// Main类中关闭
@Override 
public void stop() throws Exception {
    Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Exit ?", ButtonType.YES, ButtonType.NO);
    Optional<ButtonType> result = alert.showAndWait();
    
    if (result.isPresent() && result.get() == ButtonType.YES) {
        // 假设持有BlueSlime实例引用
        blueSlime.shootService.cancel();
        super.stop();
    } else {
        System.out.println("No");
        Platform.setImplicitExit(false);
    }
}

额外注意事项

  • shutdown()会拒绝新任务,但会完成已提交的任务;如果需要立即终止所有任务,可调用shutdownNow(),但可能导致任务中断,需根据业务场景选择。
  • awaitTermination()设置超时时间,等待线程池完全终止,避免程序退出后残留后台线程。

内容的提问来源于stack exchange,提问作者Ryan Sangha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:59:37