JDA Discord开发:触发条件时如何退出方法运行另一方法解决事件阻塞
问题根本原因
JDA 所有的事件回调方法默认都运行在同一个专属的事件调度线程上,你在 onMessageReceived 方法中写了最大耗时8秒的阻塞while循环,直接占住了整个事件线程,后续所有触发的事件(包括表情添加事件)都必须排队等这个循环执行完才能被调度,所以才会出现只有等超时发送times up后才响应表情回答的情况。
修复方案
核心逻辑是把超时检测逻辑从事件线程中剥离,放到独立的调度线程中运行,不阻塞正常的事件分发。
前置准备
首先定义全局调度器和保证线程可见性的状态变量:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; // 全局只需要初始化一个调度器即可 private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // 加volatile关键字保证多线程场景下的状态可见性 private volatile String answered = "notanswered";
修改onMessageReceived方法
移除原来的阻塞循环,替换为延迟调度任务:
public void onMessageReceived(MessageReceivedEvent event) { String[] messagelements = event.getMessage().getContentRaw().split("\\s+"); if (event.getMessage().getContentRaw().equalsIgnoreCase("what is the number beside " + wordanswer) && event.getAuthor().isBot()){ // 重置答题状态 answered = "notanswered"; // 提交8秒延迟的超时检测任务,完全不占用当前事件线程 scheduler.schedule(() -> { if ("notanswered".equalsIgnoreCase(answered)) { event.getChannel().sendMessage("times up").queue(); } }, 8, TimeUnit.SECONDS); } }
修改onMessageReactionAdd方法
删除没必要的线程优先级修改逻辑,其余逻辑保持不变即可:
public void onMessageReactionAdd (MessageReactionAddEvent event) { if (event.getUser().equals(worker)) { if ("notanswered".equalsIgnoreCase(answered)){ if (event.getReactionEmote().getName().equalsIgnoreCase(emoteanswer)){ event.getChannel().sendMessage("Correct!").queue(); } else { event.getChannel().sendMessage("Wrong").queue(); } answered = "answered"; } } }
额外注意事项
- 如果需要同时支持多频道/多场次的答题,不要用全局的
answered变量,改为用ConcurrentHashMap<Long, String>按频道ID、用户ID等维度存储答题状态,避免不同场次的状态互相冲突。 - 应用停止运行时记得调用
scheduler.shutdown()关闭调度器,避免JVM无法正常退出。 - 不要在任何JDA的事件回调方法中写长时间阻塞的逻辑,所有耗时、延迟类任务都要放到独立线程中执行,这是JDA开发的通用准则。
内容的提问来源于stack exchange,提问作者Isaac
相关产品推荐
相关产品推荐

