面试题答案
一键面试BlockingQueue的阻塞机制
- 入队阻塞:当
BlockingQueue
已满时,如果再调用put(E e)
方法添加元素,调用线程会被阻塞,直到队列有空间可用。例如,ArrayBlockingQueue
初始化时指定了容量,当队列达到该容量后,后续的put
操作将阻塞。 - 出队阻塞:当
BlockingQueue
为空时,调用take()
方法获取元素,调用线程会被阻塞,直到队列中有元素可取。比如LinkedBlockingQueue
,在队列为空时take
操作会等待新元素进入队列。
与普通Queue的主要区别
- 操作特性:
- 普通Queue:
add(E e)
方法在队列满时会抛出IllegalStateException
,offer(E e)
方法在队列满时返回false
,remove()
和poll()
方法在队列为空时分别抛出NoSuchElementException
和返回null
,不具备阻塞特性。 - BlockingQueue:
put(E e)
和take()
方法在满足特定条件(队列满或空)时会阻塞线程,直到条件满足,提供了更优雅的线程同步机制。
- 普通Queue:
- 应用场景:
- 普通Queue:适用于不需要考虑线程阻塞,对队列操作的及时性要求不高,并且希望在操作失败时通过异常或返回值来处理的场景。
- BlockingQueue:适用于需要在多线程环境下安全地共享数据,并且线程需要在队列状态变化时进行等待和唤醒的场景。
优先使用BlockingQueue的场景
- 生产者 - 消费者模型:在生产者 - 消费者模式中,生产者将数据放入
BlockingQueue
,消费者从队列中取出数据。当队列满时,生产者线程阻塞等待队列有空间;当队列空时,消费者线程阻塞等待队列有数据。例如,在一个日志处理系统中,日志生成线程(生产者)将日志信息放入BlockingQueue
,日志存储线程(消费者)从队列中取出日志并写入文件。 - 线程池任务队列:线程池中的任务队列可以使用
BlockingQueue
。当提交的任务数超过线程池的核心线程数时,任务会被放入BlockingQueue
。如果队列已满,新的任务提交会根据线程池的拒绝策略处理,而BlockingQueue
的阻塞特性可以有效地控制任务的流入速度,避免任务堆积导致系统资源耗尽。