面试题答案
一键面试1. Java中同步队列(以LinkedBlockingQueue为例)工作原理
- 数据结构:LinkedBlockingQueue是基于链表结构实现的有界队列(也可以通过构造函数设置为无界)。它内部维护了一个链表,用于存储元素。
- 入队操作:
- 当调用
put(E e)
方法将元素入队时,如果队列已满(有界队列情况下),调用线程会被阻塞,直到队列有可用空间。这是通过Condition
条件变量实现的。具体来说,LinkedBlockingQueue
内部有一个notFull
的Condition
,当队列满时,调用put
方法的线程会在notFull.await()
处等待,直到其他线程从队列中取出元素后,通过notFull.signal()
唤醒等待的线程。 - 如果队列未满,新元素会被添加到链表的尾部。
- 当调用
- 出队操作:
- 当调用
take()
方法从队列中取出元素时,如果队列为空,调用线程会被阻塞,直到队列中有元素。这里使用了notEmpty
的Condition
,线程在notEmpty.await()
处等待,当其他线程向队列中添加元素后,通过notEmpty.signal()
唤醒等待的线程。 - 如果队列不为空,从链表头部取出元素。
- 当调用
2. 对线程池(如ThreadPoolExecutor)性能的影响
- 任务缓冲:
- 线程池中的任务队列通常使用同步队列(如LinkedBlockingQueue)。当线程池中的核心线程都在忙碌时,新提交的任务会被放入队列中等待执行。LinkedBlockingQueue的有界性可以防止任务无限堆积,避免内存溢出等问题。如果设置为无界队列,在高负载情况下,任务可能会不断堆积,导致内存占用持续上升,影响系统性能甚至导致系统崩溃。
- 线程创建与复用:
- 如果队列未满,新任务会进入队列等待现有线程处理,而不会立即创建新线程(前提是当前线程数小于最大线程数)。这有助于线程复用,减少线程创建和销毁的开销,提高性能。例如,当任务处理速度较快且任务量持续稳定时,核心线程可以不断从队列中获取任务执行,避免频繁创建和销毁线程。
- 当队列已满且当前线程数小于最大线程数时,线程池会创建新的线程来处理任务。如果队列设置不合理(如容量过小),可能导致线程频繁创建和销毁,增加系统开销,降低性能。
- 线程阻塞与唤醒:
- 由于LinkedBlockingQueue的入队和出队操作可能导致线程阻塞,这在一定程度上会影响线程池的性能。例如,当任务处理速度较慢,队列已满,新任务入队时线程阻塞等待,可能会使提交任务的线程响应变慢。而在队列从空到有元素时,需要唤醒等待的线程,这也存在一定的上下文切换开销。合理设置队列容量以及线程池参数(如核心线程数、最大线程数等),可以尽量减少这种阻塞和唤醒带来的性能损耗。