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

Java线程无法正确从List移除自身致程序冻结?求排查修复

问题原因分析
  1. 非线程安全集合的并发损坏:如果你的passengersOnBoard使用了ArrayList这类非线程安全集合,多线程下的add/remove/set操作会破坏集合内部结构(比如扩容时的数组拷贝、元素移位错误),直接产生null元素。
  2. 原子性操作缺失:乘客下船的核心逻辑(从列表移除自身+状态同步)没有作为原子操作执行。比如乘客先将列表中自身位置设为null,还未完成移除,BoatKeeper就开始遍历检查,误将null当作未下船的乘客,进入无限等待。
  3. 调试时序掩盖问题:调试时的断点会强制线程暂停,让原本混乱的并发操作变得有序,竞态条件无法触发,因此问题“消失”。
修复方案

1. 替换为线程安全集合

将非线程安全的列表替换为CopyOnWriteArrayList(适合读多写少场景)或ConcurrentLinkedQueue(适合队列操作场景),这类集合原生处理并发修改,避免结构损坏:

// 替换原ArrayList定义
private List<Passenger> passengersOnBoard = new CopyOnWriteArrayList<>();

2. 保证核心操作的原子性

对乘客上下船、BoatKeeper检查列表的操作加锁,确保“修改集合+状态通知”是原子执行的,避免中间状态被读取:

// 乘客下船方法
public void disembark() {
    synchronized (passengersOnBoard) {
        passengersOnBoard.remove(this);
        // 通知BoatKeeper状态变化
        passengersOnBoard.notifyAll();
    }
}

// BoatKeeper等待下船完成的逻辑
public void waitForAllDisembark() throws InterruptedException {
    synchronized (passengersOnBoard) {
        while (!passengersOnBoard.isEmpty()) {
            passengersOnBoard.wait();
        }
    }
}

3. 修正Semaphore与乘客状态的绑定逻辑

确保Semaphore的许可数量与实际在船乘客严格对应:

  • 乘客上船前必须获取Semaphore许可,获取成功后再加入集合;
  • 乘客下船后必须先从集合移除,再释放Semaphore许可;
  • BoatKeeper的等待逻辑可以结合Semaphore的可用许可(availablePermits())+ 集合状态双重校验,避免单一条件判断的漏洞。

4. 禁止用null标记已下船乘客

不要用null作为乘客已下船的占位符,直接从集合中移除对应乘客对象,或给Passenger类添加isOnBoard布尔字段,BoatKeeper通过字段状态判断,而非检查null。

内容的提问来源于stack exchange,提问作者Sergey Zolotarev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 16:03:21