Java多线程环境下Ping Minecraft服务器偶尔返回离线问题求助
问题根因
- 并发连接资源耗尽:从报错的线程编号
Thread-16145可判断你启动的线程数过高,操作系统对单个进程的并发出站连接数、临时端口分配数量有默认上限,批量高并发发起连接时,系统来不及处理TCP握手、分配端口就会触发连接超时,和应用层配置的超时阈值无关,单测低并发场景自然不会触发该问题。 - 工具类非线程安全+无空值校验:你使用的
MinecraftPing工具如果内部存在共享静态变量、未隔离的连接资源,多线程同时调用会导致内部状态紊乱,要么触发连接异常,要么返回不完整的MinecraftPingReply对象,你直接链式调用data.getPlayers().getOnline()未做逐层空判断,就会抛出空指针异常,且当前catch块未捕获NullPointerException,会直接导致线程异常退出。 - 无失败重试机制:单次连接失败可能是网络抖动、目标服务器临时连接过载导致,直接判定为离线自然会出现误判。
解决方案
- 限制并发数
不要直接批量创建线程,改用固定大小的线程池控制并发,参考配置:
// 并发数根据服务器配置、目标站点承受能力调整,建议控制在20-50之间 ExecutorService pingPool = Executors.newFixedThreadPool(32); // 提交所有Ping任务 for (String ip : ipList) { pingPool.submit(new PingServer(ip)); } pingPool.shutdown();
- 补充空值校验+完善异常捕获
修改PingServer的run方法,增加重试逻辑避免偶发误判:
public void run() { int retryTimes = 3; // 最多重试3次 boolean success = false; for (int i = 0; i < retryTimes && !success; i++) { try { // 每次新建独立的MinecraftPing实例,避免共享状态异常 MinecraftPingReply data = new MinecraftPing().getPing(new MinecraftPingOptions().setHostname(serverip).setPort(25565).setTimeout(5000)); if (data != null && data.getPlayers() != null) { String servercount = data.getPlayers().getOnline() + "/" + data.getPlayers().getMax(); System.out.println(serverip + " " + servercount + " pineapple"); success = true; } } catch (IOException | IllegalStateException | IllegalArgumentException | JsonSyntaxException | NullPointerException exception){ // 最后一次重试还失败再打印离线 if (i == retryTimes - 1) { System.out.println("offline" + serverip + " " + exception); } else { // 重试间隔300ms,避免请求太密集 try { Thread.sleep(300); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } }
- 可选优化:调整系统连接参数
如果需要更高的并发量,可修改操作系统内核参数:
- Linux系统调整
/etc/sysctl.conf的net.ipv4.ip_local_port_range扩大临时端口范围,开启net.ipv4.tcp_tw_reuse快速回收TIME_WAIT状态的连接 - Windows系统修改注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters下的TcpNumConnections项提升最大连接数限制
内容的提问来源于stack exchange,提问作者Sacha Lemoyne
相关产品推荐
相关产品推荐

