Java LinkedBlockingQueue面试题

1. 什么是LinkedBlockingQueue?

LinkedBlockingQueue是一个基于链表结构的阻塞队列,使用ReentrantLock和Condition来实现线程同步,是一个FIFO(先进先出)队列。

LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();

2. LinkedBlockingQueue的主要特点是什么?

  • 线程安全:支持多个线程同时访问。
  • 阻塞操作:当队列为空时,从队列中取数据的操作会被阻塞;当队列满时,向队列中添加数据的操作会被阻塞。
  • 容量限制:可以设置容量上限,也可以设置为无界。
LinkedBlockingQueue<Integer> queueWithCapacity = new LinkedBlockingQueue<>(10); // 有界队列
LinkedBlockingQueue<Integer> queueWithoutCapacity = new LinkedBlockingQueue<>(); // 无界队列

3. LinkedBlockingQueue如何添加元素?

使用put方法添加元素,如果队列满了,会阻塞直到队列中有空间。

boolean added = queue.offer(1); // 添加元素,如果队列满了会返回false

4. LinkedBlockingQueue如何移除元素?

使用take方法移除元素,如果队列为空,会阻塞直到队列中有元素。

Integer element = queue.take(); // 移除并返回队列头部元素,如果队列为空会阻塞

5. LinkedBlockingQueue的迭代器是否是弱一致性的?

是的,LinkedBlockingQueue的迭代器是弱一致性的,它只能反映结构性修改,不能反映由并发一致性条件所发生的迭代器创建后的变化。

Iterator<Integer> iterator = queue.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
}

6. LinkedBlockingQueue的poll和peek方法有什么区别?

  • poll:移除并返回队列头部的元素,如果队列为空则返回null
  • peek:返回队列头部的元素但不移除,如果队列为空则返回null
Integer polledElement = queue.poll(); // 移除并返回队列头部元素
Integer peekedElement = queue.peek(); // 返回队列头部元素但不移除

7. LinkedBlockingQueue是否允许null元素?

是的,LinkedBlockingQueue允许null元素。

queue.put(null); // 允许添加null元素

8. LinkedBlockingQueue的容量有什么限制?

如果构造时指定了容量,则队列的最大容量为该值;如果未指定容量,则队列为无界队列。

9. LinkedBlockingQueue如何实现线程安全的队列操作?

通过内置锁和条件变量来实现线程安全的队列操作。

10. LinkedBlockingQueue的size方法有什么特点?

返回队列中的元素数量,但是一个近似值,因为队列是并发访问的。

int size = queue.size(); // 返回队列大小

11. LinkedBlockingQueue的isEmpty方法是如何工作的?

返回队列是否为空。

boolean empty = queue.isEmpty(); // 检查队列是否为空

12. LinkedBlockingQueue如何实现生产者-消费者模型?

生产者使用put方法添加元素,消费者使用take方法移除元素。

// 生产者
new Thread(() -> {
    try {
        queue.put(1);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}).start();

// 消费者
new Thread(() -> {
    try {
        Integer element = queue.take();
        System.out.println(element);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}).start();

13. LinkedBlockingQueue的剩余容量是多少?

使用remainingCapacity方法返回队列的剩余容量。

int remainingCapacity = queue.remainingCapacity(); // 返回队列的剩余容量

14. LinkedBlockingQueue是否支持公平性?

构造时可以指定是否是公平性队列。公平性队列按照线程等待时间的长短来分配队列资源。

LinkedBlockingQueue<Integer> fairQueue = new LinkedBlockingQueue<>(10, true); // 公平性队列

15. LinkedBlockingQueue的clear方法有什么作用?

清除队列中的所有元素。

queue.clear(); // 清除队列中的所有元素

16. LinkedBlockingQueue的drainTo方法有什么作用?

将队列中的所有元素转移到另一个集合中。

List<Integer> list = new ArrayList<>();
int drainedCount = queue.drainTo(list); // 将队列中的所有元素转移到list中

17. LinkedBlockingQueue的put和offer有什么区别?

  • put:如果队列满了,会阻塞直到队列中有空间。
  • offer:如果队列满了,会返回false而不阻塞。
try {
    queue.put(1); // 队列满了会阻塞
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

boolean offered = queue.offer(1); // 队列满了会返回false

18. LinkedBlockingQueue的take和poll有什么区别?

  • take:如果队列为空,会阻塞直到队列中有元素。
  • poll:如果队列为空,会返回null而不阻塞。
try {
    Integer element = queue.take(); // 队列为空会阻塞
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

Integer polledElement = queue.poll(); // 队列为空会返回null

19. LinkedBlockingQueue如何实现FIFO?

通过链表结构实现FIFO,队列头部是最先添加的元素,队列尾部是最后添加的元素。

20. LinkedBlockingQueue的数组复制开销大吗?

在高并发写操作时,数组复制的开销可能较大,因为每次修改操作都需要复制整个数组。