Appearance
调度器(任务调度)
概述
Bngeuit 提供内置的任务调度器,支持同步和异步两种任务模式:
- 同步任务(
runSync):在专用单线程scheduler-sync上串行执行,任务之间天然线程安全 - 异步任务(
runAsync):在线程池scheduler-async(4 线程)中并发执行,适合 I/O 密集型操作
所有延迟/周期参数单位为毫秒。
快速上手
获取调度器
java
import cn.org.bukkit.bngeuit.Bngeuit;
import cn.org.bukkit.bngeuit.scheduler.BngeuitScheduler;
BngeuitScheduler scheduler = Bngeuit.getScheduler();立即执行
java
// 同步任务(与事件处理串行,线程安全)
scheduler.runSync(plugin, () -> {
// 直接操作游戏状态,无需加锁
System.out.println("同步执行");
});
// 异步任务(线程池并发)
scheduler.runAsync(plugin, () -> {
// 适合网络请求、文件 I/O 等
String result = httpGet("https://api.example.com/data");
});延迟执行
java
// 5 秒后同步执行
scheduler.runSyncLater(plugin, () -> {
Bngeuit.sendGroupText(groupOpenid, "延迟消息");
}, 5000);
// 3 秒后异步执行
scheduler.runAsyncLater(plugin, () -> {
// 异步 I/O 操作
}, 3000);定时任务
java
// 每 60 秒执行一次(首次延迟 10 秒)
scheduler.runSyncTimer(plugin, () -> {
Bngeuit.sendGroupText(groupOpenid, "定时消息");
}, 10000, 60000);
// 每 5 分钟异步轮询
scheduler.runAsyncTimer(plugin, () -> {
checkAndProcessQueue();
}, 0, 300000);取消任务
java
import cn.org.bukkit.bngeuit.scheduler.BngeuitTask;
// 保存任务句柄
BngeuitTask task = scheduler.runSyncTimer(plugin, () -> { ... }, 0, 60000);
// 取消单个任务
task.cancel();
// 或通过 ID 取消
scheduler.cancel(task.getTaskId());
// 取消插件的所有任务
scheduler.cancelTasks(plugin);查询任务
java
import java.util.List;
List<BngeuitTask> tasks = scheduler.getActiveTasks(plugin);
for (BngeuitTask t : tasks) {
System.out.printf("任务 #%d [%s] 已取消: %s%n",
t.getTaskId(), t.isSync() ? "同步" : "异步", t.isCancelled());
}API 参考
BngeuitScheduler
任务调度器接口。
任务提交
| 方法 | 说明 |
|---|---|
runSync(owner, task) | 立即同步执行 |
runSyncLater(owner, task, delayMs) | 延迟同步执行 |
runSyncTimer(owner, task, delayMs, periodMs) | 定时同步执行 |
runAsync(owner, task) | 立即异步执行 |
runAsyncLater(owner, task, delayMs) | 延迟异步执行 |
runAsyncTimer(owner, task, delayMs, periodMs) | 定时异步执行 |
runSystemSync(task) 等 runSystem* 系列 | 框架内部系统任务(owner 固定 null,不随插件卸载清理),插件不可使用 |
参数说明:
owner:提交任务的插件实例(必填,传this;传null直接抛IllegalArgumentException—— null 任务的插件无法随卸载取消)task:要执行的RunnabledelayMs:首次执行延迟(毫秒)periodMs:重复间隔(毫秒)
受管执行器(插件后台线程的正解)
| 方法 | 说明 |
|---|---|
getPluginExecutor(owner) | 获取插件专属的受管执行器(懒创建,每插件一个 cached daemon 线程池),插件卸载时框架自动 shutdownNow() |
shutdownPluginExecutors(owner) | 关闭插件名下的全部受管执行器(卸载时自动调用,无需手动) |
也可直接用 JavaPlugin.getExecutor()(等价于 getPluginExecutor(this))。不要自建 Thread / ExecutorService —— 自建线程无法被框架追踪,插件卸载后会在后台残留;确需自建时必须在 onDisable 中自行停止(详见下方「插件卸载」)。
任务管理
| 方法 | 说明 |
|---|---|
cancel(taskId) | 按 ID 取消任务 |
cancelTasks(plugin) | 取消指定插件的所有任务(插件卸载时自动调用) |
getActiveTasks(plugin) | 查询指定插件的活跃任务列表 |
shutdown() | 关闭调度器(取消所有任务,关闭线程池) |
BngeuitTask
任务句柄接口。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getTaskId() | int | 全局唯一任务 ID |
getOwner() | Plugin | 提交任务的插件,系统任务为 null |
isSync() | boolean | 是否为同步任务 |
isCancelled() | boolean | 是否已取消或已完成 |
cancel() | void | 取消任务(不会中断正在执行的任务) |
除通过任务句柄查询外,也可用调度器的 getActiveTasks(plugin) 按插件批量查询其活动任务。
同步 vs 异步
| 特性 | 同步任务 | 异步任务 |
|---|---|---|
| 执行线程 | 专用单线程 scheduler-sync 串行 | 线程池 scheduler-async(4 线程)并发 |
| 线程安全 | 任务之间天然串行安全 | 需自行处理同步 |
| 适用场景 | 需要顺序执行的定时任务 | 网络请求、文件 I/O |
| 阻塞影响 | 阻塞后续 sync 任务 | 不影响 sync 线程 |
注意
同步任务在独立的 scheduler-sync 线程上执行,与事件处理线程不同。但 sync 任务之间是串行的,长时间运行的 sync 任务会阻塞后续 sync 任务。与事件处理线程之间仍需注意并发安全。
插件卸载
插件卸载时,框架自动完成(无需手动清理):
cancelTasks(plugin)—— 取消该插件的所有调度任务;- 关闭插件的受管执行器(
getPluginExecutor创建的线程池,shutdownNow()); - 扫描仍在执行插件代码的自建线程并发送中断 + 告警(兜底手段)。
系统任务(runSystem* 提交)不受插件卸载影响。
自建线程
JVM 无法强制终止线程,框架也拦截不了 new Thread() —— 卸载时的扫描中断只是兜底(若线程不响应中断,仍会残留)。自建线程必须遵守两条约定:setDaemon(true),并在 onDisable 中自行停止;能改用受管执行器或 runAsyncTimer 的优先改用。
完整示例
java
import cn.org.bukkit.bngeuit.Bngeuit;
import cn.org.bukkit.bngeuit.scheduler.*;
import cn.org.bukkit.bngeuit.event.SubscribeEvent;
import cn.org.bukkit.bngeuit.event.receive.GroupAtMessageEvent;
public class SchedulerExample {
private BngeuitTask heartbeatTask;
// 启动心跳定时任务
public void startHeartbeat(Plugin plugin, String groupOpenid) {
BngeuitScheduler scheduler = Bngeuit.getScheduler();
heartbeatTask = scheduler.runSyncTimer(plugin, () -> {
Bngeuit.sendGroupText(groupOpenid, "机器人在线 ✓");
}, 0, 300000); // 每 5 分钟
}
// 停止心跳
public void stopHeartbeat() {
if (heartbeatTask != null) {
heartbeatTask.cancel();
}
}
// 异步请求示例
@SubscribeEvent
public void onCommand(GroupAtMessageEvent e) {
Plugin plugin = ...; // 获取当前插件
BngeuitScheduler scheduler = Bngeuit.getScheduler();
// 异步获取数据,完成后直接发送消息
scheduler.runAsync(plugin, () -> {
String data = fetchDataFromAPI();
// API 方法本身是线程安全的,无需切回 sync 线程
Bngeuit.replyGroupText(e.getGroupOpenid(), e.getId(), 1, data);
});
}
}