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

Java线程wait/notify异常:自定义连接池唤醒后列表为空

自定义连接池唤醒后连接为空问题的解决方案

问题根源

  1. 内置锁的非公平性:你用synchronized的notify()会随机唤醒一个等待线程,但释放锁后,刚归还连接的线程(比如Thread6)会和被唤醒的线程(比如Thread0)竞争锁。由于内置锁是非公平的,归还线程可能优先抢到锁,把刚归还的连接再次取走,导致被唤醒的线程拿到锁时列表为空。
  2. 未循环检查等待条件:getConnection里用if判断列表是否为空,被唤醒后直接尝试取连接,没有重新检查条件,一旦连接被其他线程抢走就会出错。
  3. volatile在这里无效:synchronized块已经保证了对列表的内存可见性,volatile修饰列表不会解决锁竞争和唤醒顺序的问题。

解决步骤

要实现按等待顺序分配连接,避免归还线程抢占,需要用公平锁+Condition替代内置的synchronized/wait/notify,同时循环检查等待条件:

1. 替换为公平锁和Condition

用ReentrantLock(公平模式)保证线程按等待顺序获取锁,用Condition的await()和signal()来控制唤醒顺序(只唤醒等待最久的线程)。

2. 循环检查等待条件

在getConnection中用while而非if判断连接是否为空,确保被唤醒后重新检查条件,避免连接被抢占后出错。

完整代码示例

public class CustomConnectionPool {
    private static volatile CustomConnectionPool instance;
    private final LinkedList<SocketAdapter> availableConnections;
    private final ReentrantLock fairLock;
    private final Condition connectionAvailable;

    private CustomConnectionPool() {
        availableConnections = new LinkedList<>();
        // 初始化公平锁,保证线程按等待顺序获取锁
        fairLock = new ReentrantLock(true);
        connectionAvailable = fairLock.newCondition();
    }

    // 双重检查锁实现单例
    public static CustomConnectionPool getInstance() {
        if (instance == null) {
            synchronized (CustomConnectionPool.class) {
                if (instance == null) {
                    instance = new CustomConnectionPool();
                }
            }
        }
        return instance;
    }

    public SocketAdapter getConnection() throws InterruptedException {
        fairLock.lock();
        try {
            // 循环检查:被唤醒后必须确认连接不为空
            while (availableConnections.isEmpty()) {
                connectionAvailable.await();
            }
            // 从队列头部取连接,保证FIFO分配
            return availableConnections.removeFirst();
        } finally {
            fairLock.unlock();
        }
    }

    public void returnConnection(SocketAdapter connection) {
        fairLock.lock();
        try {
            availableConnections.addLast(connection);
            // 只唤醒等待队列的第一个线程,保证顺序
            connectionAvailable.signal();
        } finally {
            fairLock.unlock();
        }
    }
}

为什么这样有效

  • 公平锁:ReentrantLock(true)会让等待最久的线程优先获取锁,避免刚释放锁的线程立刻抢占。
  • Condition的signal():只会唤醒等待队列中的第一个线程,不会随机唤醒,保证等待顺序。
  • while循环检查:即使被唤醒后连接被其他线程抢走,会重新进入等待,避免空列表的情况。

内容的提问来源于stack exchange,提问作者Lucas Fabiano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 08:23:02