MST

星途 面试题库

面试题:Java中BlockingQueue线程安全机制相关 - 基础原理

请简要阐述BlockingQueue是如何保证线程安全的,结合常见的实现类如ArrayBlockingQueue说明。
26.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

BlockingQueue保证线程安全的方式

  1. 使用锁机制:BlockingQueue 通常会使用 ReentrantLock 等锁来保证对队列数据结构的访问是线程安全的。锁可以确保同一时间只有一个线程能够对队列进行操作,避免多个线程同时修改队列状态导致的数据不一致问题。
  2. 条件变量(Condition):配合锁机制,使用 Condition 来实现线程的等待和唤醒。当队列满时,尝试添加元素的线程会等待在对应的条件变量上;当队列空时,尝试获取元素的线程也会等待在相应条件变量上。当队列状态改变(如队列有空间或有元素)时,会唤醒等待在条件变量上的线程。

以ArrayBlockingQueue为例

  1. 锁的使用ArrayBlockingQueue 内部使用 ReentrantLock 来保证线程安全。例如,在 put 方法(向队列添加元素)和 take 方法(从队列获取元素)中,首先会获取锁:
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

这里通过 lock.lockInterruptibly() 获取锁,保证在获取锁的过程中线程可以被中断。 2. 条件变量的使用ArrayBlockingQueue 有两个 Condition 对象,notEmptynotFullnotFull 用于队列满时,添加元素的线程等待;notEmpty 用于队列空时,获取元素的线程等待。如上述 put 方法中,当 count == items.length(队列满)时,线程调用 notFull.await() 进入等待状态,直到其他线程从队列取出元素后唤醒等待在 notFull 上的线程。在 take 方法中类似:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

count == 0(队列空)时,线程调用 notEmpty.await() 等待,直到其他线程向队列添加元素后唤醒等待在 notEmpty 上的线程。通过这种锁和条件变量的配合使用,ArrayBlockingQueue 实现了线程安全的阻塞队列功能。