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

如何同步Timer的cancel()方法,确保first()优先执行?

问题与解决方案

问题背景

需要实现Timer.cancel()后,确保first()方法优先于second()执行。原代码中,Timer调度的任务执行长耗时逻辑后调用first(),但调用print()时,timer.cancel()后直接执行second(),无法保证两者的执行顺序。尝试过空同步块方案,但该方式可读性差、不直观,并非合理方案。

问题分析

Timer.cancel()仅会终止后续任务的调度,不会中断正在运行的TimerTask线程。因此print()中的second()与TimerTask中的first()可能并发执行,导致顺序无法保证。空同步块虽然能通过锁的happens-before规则勉强实现顺序,但属于不直观的“黑魔法”,维护性极差,不推荐使用。

正确解决方案

方案1:使用CountDownLatch(推荐)

利用CountDownLatch专门等待线程任务完成的特性,明确控制执行顺序:

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;

public class TimerSyncDemo {
    private Timer timer;
    private CountDownLatch taskLatch;

    public void startTimer(long delayNoMatter, long veryShortInterval) {
        taskLatch = new CountDownLatch(1);
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    // 模拟长耗时任务
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    // 响应中断,避免任务无意义挂起
                    Thread.currentThread().interrupt();
                    return;
                }
                first();
                taskLatch.countDown(); // 任务完成,释放等待信号
            }
        }, delayNoMatter, veryShortInterval);
    }

    void first() {
        System.out.println("first");
    }

    void second() {
        // 必须最后执行
        System.out.println("second");
    }

    void print() {
        timer.cancel(); // 终止后续任务调度
        try {
            taskLatch.await(); // 等待正在运行的任务执行完first()
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        second();
    }
}

说明:

  • CountDownLatch初始化计数为1,TimerTask执行完first()后调用countDown()将计数减为0
  • print()中调用await()会阻塞,直到计数为0,确保second()在first()之后执行

方案2:用ScheduledExecutorService替代Timer(更优)

Timer存在单线程局限性(单个任务异常会导致整个Timer终止),推荐使用ScheduledExecutorService替代,结合同步工具实现顺序控制:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CountDownLatch;

public class ExecutorSyncDemo {
    private ScheduledExecutorService executor;
    private CountDownLatch taskLatch;

    public void startTimer(long delayNoMatter, long veryShortInterval) {
        taskLatch = new CountDownLatch(1);
        executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    // 模拟长耗时任务
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
                first();
                taskLatch.countDown();
            }
        }, delayNoMatter, veryShortInterval, TimeUnit.MILLISECONDS);
    }

    void first() {
        System.out.println("first");
    }

    void second() {
        System.out.println("second");
    }

    void print() {
        executor.shutdown(); // 停止接受新任务
        try {
            // 等待正在运行的任务完成,超时时间可按需调整
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow(); // 超时后强制终止任务
            }
            taskLatch.await(); // 确保first()已执行完毕
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        second();
    }
}

说明:

  • ScheduledExecutorService是Timer的官方替代方案,支持多线程调度,稳定性更强
  • shutdown()不会中断正在运行的任务,awaitTermination()可等待任务完成,结合CountDownLatch双重保障执行顺序

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:41:01