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
相关产品推荐
相关产品推荐

