如何在Minecraft插件中实现每秒执行一次的while函数(禁用Thread.sleep())
嘿,这个坑我踩过好多次!Minecraft插件开发里绝对不能用Thread.sleep(),一用整个服务器都得卡成PPT。给你几个靠谱的替代方案,都是圈内常用的:
方案1:用 Bukkit/Paper 同步调度器(最常用)
Bukkit API 自带的调度器是处理这类定时任务的首选,它能在服务器主线程安全地按间隔重复执行逻辑,完全不会阻塞整个进程。核心是用runTaskTimer()替代while循环,它会帮你自动管理任务的重复执行,直到你主动取消。
举个实际代码例子:
// 先获取调度器实例 BukkitScheduler scheduler = Bukkit.getScheduler(); // 启动定时任务:延迟0秒立刻开始,每1秒(对应20个游戏tick,MC服务器每秒跑20 tick)执行一次 int taskId = scheduler.runTaskTimer(yourPluginInstance, new Runnable() { @Override public void run() { // 这里写你要每秒执行的核心逻辑 // 比如给在线玩家发提示消息 Bukkit.broadcastMessage("这是每秒执行一次的任务!"); } }, 0L, 20L); // 要是后续需要停止任务(比如满足某个条件时),调用这个就行 // scheduler.cancelTask(taskId);
⚠️ 注意:这个是同步任务,所以里面的逻辑不能太耗时(比如复杂计算、IO操作),不然还是会拖慢服务器。如果有耗时操作,看下面的异步方案。
方案2:异步调度器(处理耗时逻辑)
如果你的每秒逻辑里有数据库查询、网络请求这类费时间的操作,必须用异步任务,避免卡住主线程。用runTaskTimerAsynchronously()方法就行:
int asyncTaskId = scheduler.runTaskTimerAsynchronously(yourPluginInstance, new Runnable() { @Override public void run() { // 这里放耗时的异步逻辑,比如查数据库 String data = someDatabase.query("SELECT ..."); // 重点:异步线程里不能直接操作游戏内的实体、方块! // 要是需要修改游戏内容,得切回主线程: scheduler.runTask(yourPluginInstance, () -> { // 在这里安全操作游戏对象,比如给玩家背包加物品 Player player = Bukkit.getPlayer("PlayerName"); if (player != null) { player.sendMessage("从数据库拿到的数据:" + data); } }); } }, 0L, 20L);
方案3:BukkitRunnable 封装写法(更简洁)
Bukkit还提供了BukkitRunnable类,写法更清爽,本质和调度器是同一个东西,适合需要在任务里判断停止条件的场景:
new BukkitRunnable() { @Override public void run() { // 你的每秒执行逻辑 if (someStopCondition) { // 比如某个玩家下线了,或者任务完成了 this.cancel(); // 满足条件就停止任务 } } }.runTaskTimer(yourPluginInstance, 0L, 20L);
几个关键提醒
- 任务不用了一定要取消,不然插件卸载后任务还会继续跑,导致内存泄漏。
- 永远记住MC服务器的tick是20/秒,所以间隔参数填20就是1秒,别搞成1了!
- 异步线程绝对不能直接操作游戏内对象,必须通过
scheduler.runTask()切回主线程操作。
内容的提问来源于stack exchange,提问作者Tomáš Soušek




