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

Java Swing任务定时器问题:子任务计时器无法正常停止

问题排查与解决方案

我仔细梳理了你的代码,发现子任务计时器无法停止的根源主要是两个核心问题,还有几个线程安全相关的细节需要调整:

1. 鼠标监听器的Switch-Case缺少Break语句

这是最关键的触发点!你的GUI任务鼠标点击事件处理里,switch-case没有添加break,导致多个分支被意外执行:

  • 当顶级任务处于UNDEF状态时,用户点击启动任务后,代码会继续执行STOPPED分支的resumeTask()方法
  • 这会导致同一个任务被重复启动,创建多个独立的Timer线程——这些线程各自持有自己的run变量,后续调用stopTask()只能停止最后一个创建的Timer线程,旧线程的run始终为true,会持续循环计时

2. Timer重复启动未清理旧线程

  • TaskObject的resumeTask()直接调用timer.start(),而Timer的start()方法每次都会新建线程
  • 如果之前的Timer线程没有被正确停止,就会同时存在多个线程累加currentMillis,且旧线程因为对应的Timer实例可能已被覆盖,无法被停止

修复方案

第一步:修复Switch-Case的Break问题

在GUI的鼠标监听器中,给每个case分支添加break,避免分支穿透:

task.addMouseListener(new MouseAdapter(){
    public void mousePressed(MouseEvent me) {
        if(me.getButton() == MouseEvent.BUTTON1){
            switch(task.getState()){
                case UNDEF:
                    int answer = JOptionPane.showConfirmDialog(GUI.this, "Do you want to start this task?", "Confirmation", JOptionPane.OK_CANCEL_OPTION);
                    if(answer == JOptionPane.OK_OPTION){
                        task.startTask();
                    }
                    break; // 新增:跳出当前分支
                case STOPPED:
                    task.resumeTask();
                    System.out.println("Resumed task");
                    break; // 新增:跳出当前分支
                case RUNNING:
                default:
                    break;
            }
        }
    }
});

子任务task2的鼠标监听器也要做同样修改。

第二步:修改Timer类,确保线程能被正确停止

调整stop()方法的逻辑顺序,先设置run=false让线程退出循环,再中断并等待线程结束;同时在start()前检查并停止旧线程:

public class Timer { 
    private Thread watchThread; 
    private long deltaTime; 
    private volatile boolean run; 
    private long time1, time2; 
    ITaskObject task; 
    
    public Timer(ITaskObject task){ 
        super(); 
        this.deltaTime = 0; 
        this.run = false; 
        this.task = task; 
    } 
    
    public void start(){ 
        // 启动前先停止已有线程,避免多线程并发
        if(watchThread != null && watchThread.isAlive()){
            stop();
        }
        createThread(); 
        run = true; 
        watchThread.start(); 
    } 
    
    public void stop(){ 
        run = false; // 先设置run为false,让线程退出循环
        if(watchThread != null && watchThread.isAlive()){
            watchThread.interrupt();
            try {
                watchThread.join(); // 等待线程彻底退出,避免残留
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 保留当前线程的中断状态
            }
        }
    } 
    
    public void reset(){ 
        run = false; 
        time1 = 0; 
        deltaTime = 0; 
    } 
    
    private void createThread(){ 
        watchThread = new Thread(() -> { 
            time1 = System.currentTimeMillis(); 
            while(run){ 
                time2 = System.currentTimeMillis(); 
                deltaTime = time2 - time1; 
                task.addTime((double)deltaTime); 
                time1 = time2; 
                // 添加短暂休眠,减少CPU占用(可选但推荐)
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            } 
        }); 
        watchThread.setDaemon(true); // 设置为守护线程,避免阻塞程序退出
    } 
} 

第三步:确保TaskObject的线程安全

给addTime()方法添加synchronized修饰符,避免多线程并发修改currentMillis导致的计时错误:

@Override
public synchronized void addTime(double millis){
    currentMillis += millis;
    if(currentMillis >= millisMax){
        stopTask();
        currentMillis = millisMax;
        startSubtask();
    }
    this.repaint();
}

第四步:优化TaskObject的startTask方法

在创建新Timer前,先停止旧Timer,避免残留线程:

@Override
public void startTask(){
    state = State.RUNNING;
    if(timer != null){
        timer.stop();
    }
    timer = new Timer(this);
    timer.start();
}

修复效果说明

  • 修复switch-case的break后,避免了任务被重复启动,不会创建多个Timer线程
  • Timer类的修改确保了每次启动前都会停止旧线程,且停止时能让线程正常退出循环并等待其结束
  • 同步addTime()方法避免了多线程并发修改currentMillis导致的计时错误

经过这些修改后,子任务的计时器就能在时间到达后正常停止了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 08:07:35