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

JDA开发Discord加密货币机器人:无法复用对象取消定时任务

问题:Discord加密货币定时推送任务无法取消的解决方案

我正在开发一款集成CoinMarketCap API的Discord机器人,用于查询加密货币价格并返回给用户。我实现了一个命令,触发后会在比特币收盘时间(GMT凌晨12点/巴西时间晚上9点)推送其价格,但尝试取消该定时任务时,价格仍会按时推送。

核心问题:启动命令会创建一个带有独立Timer的ScheduledAlert对象,但执行停止命令时,只能创建新的ScheduledAlert对象,无法访问启动时创建的原对象来取消任务。

相关代码如下:

//Command Handler Class
else if (command.equals("bitcoin-scheduled-alert-start")) {
            ScheduledAlert scheduledAlert = new ScheduledAlert(event.getTextChannel());
            scheduledAlert.start(LocalTime.of(21,00, 00));
            event.reply("The daily closing price of Bitcoin will be displayed from now on!").queue();

        } else if (command.equals("bitcoin-scheduled-alert-stop")) {
            ScheduledAlert disabledScheduledAlert = new ScheduledAlert(event.getTextChannel());
            disabledScheduledAlert.stop();
            event.reply("The current scheduled alert has been stopped!").queue();
        }
public class ScheduledAlert { //Bitcoin update at every candle close (12 am GMT [9 pm BRT])
    private final Timer timer;
    private TimerTask task;
    private final TextChannel channel;
    private final String symbol;

    public ScheduledAlert(TextChannel channel) {
        this.channel = channel;
        this.symbol = "BTC";
        this.timer = new Timer();
    }

    public void start(LocalTime time) { //Getting the time from BotCommands parameter
        // Cancel the task if it is already scheduled (NOT WORKING)
        if (task != null) {
            task.cancel();
        }

        task = new TimerTask() { //Start TimerTask
            @Override
            public void run() {
                System.out.println("Started");
                CryptoPrice cmcApi = new CryptoPrice(symbol); //Get Bitcoin price
                double price = cmcApi.getPrice(symbol);

                NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.US);
                String priceString = formatter.format(price);
                System.out.println((priceString));
                channel.sendMessage("The closing price of Bitcoin is " + priceString).queue();
            }
        };

        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Sao_Paulo")); //Sync date with time zone
        ZonedDateTime scheduledTime = ZonedDateTime.of(now.toLocalDate(), time, now.getZone());

        if (now.compareTo(scheduledTime) > 0) {
            scheduledTime = scheduledTime.plusDays(1); //If the command has been triggered after time in LocalTime, set it to next day at the set time
        }

        long delay = Duration.between(now, scheduledTime).toMillis();
        timer.schedule(task, delay, TimeUnit.DAYS.toMillis(1));
    }
     

    public void stop() {
        if (task != null) {
            task.cancel();
            task = null;
            System.out.println("Stopped");
        }
    }

解决方案

1. 全局存储ScheduledAlert实例

在Command Handler类中维护一个全局映射,关联每个频道对应的定时任务实例,这样启动时存入、停止时取出原实例操作:

// 在Command Handler类中添加全局映射
private Map<TextChannel, ScheduledAlert> alertMap = new HashMap<>();

// 启动命令逻辑
else if (command.equals("bitcoin-scheduled-alert-start")) {
    TextChannel channel = event.getTextChannel();
    // 先清理该频道已有的任务(如果存在)
    ScheduledAlert existingAlert = alertMap.get(channel);
    if (existingAlert != null) {
        existingAlert.stop();
        alertMap.remove(channel);
    }
    // 创建并启动新任务
    ScheduledAlert scheduledAlert = new ScheduledAlert(channel);
    scheduledAlert.start(LocalTime.of(21, 00, 00));
    alertMap.put(channel, scheduledAlert);
    event.reply("The daily closing price of Bitcoin will be displayed from now on!").queue();
} 

// 停止命令逻辑
else if (command.equals("bitcoin-scheduled-alert-stop")) {
    TextChannel channel = event.getTextChannel();
    ScheduledAlert existingAlert = alertMap.get(channel);
    if (existingAlert != null) {
        existingAlert.stop();
        alertMap.remove(channel);
        event.reply("The current scheduled alert has been stopped!").queue();
    } else {
        event.reply("No active scheduled alert found for this channel!").queue();
    }
}

2. 优化ScheduledAlert的stop方法

确保彻底释放Timer资源,避免残留线程:

public void stop() {
    if (task != null) {
        task.cancel();
        task = null;
    }
    timer.cancel(); // 取消整个Timer实例,释放资源
    System.out.println("Stopped");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 07:07:13