侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个线程安全的阻塞队列并解释其工作原理

2025-12-13 / 0 评论 / 4 阅读

题目

实现一个线程安全的阻塞队列并解释其工作原理

信息

  • 类型:问答
  • 难度:⭐⭐

考点

线程安全设计,锁机制,生产者-消费者模型,条件变量

快速回答

实现线程安全阻塞队列的核心要点:

  • 使用ReentrantLock保证操作原子性
  • 通过Condition实现精确的线程等待/唤醒机制
  • 维护两个条件变量:notEmpty(非空)和notFull(非满)
  • 数组循环存储实现队列数据结构
  • put()/take()方法中正确处理边界条件
## 解析

原理说明

阻塞队列的核心是生产者-消费者模型,当队列满时生产者线程阻塞,队列空时消费者线程阻塞。Java并发包通过ReentrantLockCondition实现:

  • 锁机制ReentrantLock保证put/take操作的原子性
  • 条件变量Condition实现精确的线程调度,比wait()/notify()更灵活
  • 循环数组:使用定长数组实现队列,通过头尾指针循环利用空间

代码示例

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class BlockingQueue<T> {
    private final T[] items;
    private int count;
    private int putIndex;
    private int takeIndex;

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    public BlockingQueue(int capacity) {
        items = (T[]) new Object[capacity];
    }

    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await();  // 队列满时阻塞生产者
            }
            items[putIndex] = item;
            if (++putIndex == items.length) putIndex = 0;  // 循环指针
            count++;
            notEmpty.signal();  // 唤醒消费者
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();  // 队列空时阻塞消费者
            }
            T item = items[takeIndex];
            items[takeIndex] = null;  // 帮助GC
            if (++takeIndex == items.length) takeIndex = 0;  // 循环指针
            count--;
            notFull.signal();  // 唤醒生产者
            return item;
        } finally {
            lock.unlock();
        }
    }
}

最佳实践

  • 双条件变量:分离空/满两种等待条件,避免无效唤醒
  • 循环数组:固定容量避免内存溢出,适合有界队列场景
  • 锁释放:务必在finally块中释放锁防止死锁
  • 中断处理:方法声明抛出InterruptedException支持响应中断

常见错误

  • 单条件变量:使用单个Condition会导致无效唤醒和性能下降
  • 未循环指针:忘记重置指针导致数组越界
  • 非线程安全操作:直接使用LinkedList等非线程安全集合
  • 锁未释放:异常路径中未释放锁造成死锁

扩展知识

  • Java内置实现ArrayBlockingQueue(有界)、LinkedBlockingQueue(可选有界)
  • 锁优化ReentrantLock支持公平/非公平锁,默认非公平锁吞吐量更高
  • 性能对比ConcurrentLinkedQueue使用CAS实现无锁队列,适合超高并发但非阻塞场景
  • 条件谓词while循环检查条件防止虚假唤醒(spurious wakeup)