面试题答案
一键面试1. Java ArrayBlockingQueue数组边界处理
- 存储数据:
ArrayBlockingQueue
内部使用一个数组来存储元素。当向队列中添加元素时,使用put
方法。在put
方法中,通过一个takeIndex
和putIndex
来记录队列的出队和入队位置。- 例如,当调用
put(E e)
方法时,如果队列已满(count == items.length
,count
为队列当前元素个数),线程会进入等待状态。否则,items[putIndex] = e
,将元素放入数组中putIndex
指向的位置,然后putIndex = inc(putIndex)
。这里的inc
方法是用于处理数组边界的关键。inc
方法通常实现为(putIndex + 1) % items.length
,这就保证了即使putIndex
到达数组末尾,也会回到数组开头,实现循环利用数组空间。
- 获取数据:
- 当从队列中获取元素时,使用
take
方法。如果队列为空(count == 0
),线程会进入等待状态。否则,E x = items[takeIndex]
,从takeIndex
指向的位置取出元素,然后takeIndex = inc(takeIndex)
,同样通过inc
方法处理数组边界,确保在获取元素时能够循环遍历数组。
- 当从队列中获取元素时,使用
2. 动态调整ArrayBlockingQueue容量的设计逻辑
- 扩容逻辑:
- 首先,需要定义一个新的更大容量的数组。例如,当前
ArrayBlockingQueue
的容量为oldCapacity
,可以定义一个新数组newItems = new Object[oldCapacity * 2]
(这里简单以扩容为原来的2倍为例,实际可根据业务需求调整扩容比例)。 - 然后,将原数组中的元素复制到新数组中。可以使用
System.arraycopy
方法,从原数组的takeIndex
位置开始复制count
个元素到新数组的起始位置。例如:System.arraycopy(items, takeIndex, newItems, 0, count)
。 - 接下来,更新
ArrayBlockingQueue
的内部状态。将items
指向新数组,即items = newItems
,并重新计算takeIndex
和putIndex
。由于元素已经复制到新数组开头,takeIndex
变为0,putIndex
变为count
。
- 首先,需要定义一个新的更大容量的数组。例如,当前
- 缩容逻辑:
- 当队列中元素个数
count
小于当前容量的一定比例(例如1/4,可根据业务需求调整),可以考虑缩容。定义一个新的更小容量的数组,例如newItems = new Object[oldCapacity / 2]
。 - 同样使用
System.arraycopy
方法将原数组中的元素复制到新数组。从takeIndex
位置开始复制count
个元素到新数组起始位置,即System.arraycopy(items, takeIndex, newItems, 0, count)
。 - 更新
ArrayBlockingQueue
的内部状态,items
指向新数组,takeIndex
变为0,putIndex
变为count
。
- 当队列中元素个数
在整个动态调整容量的过程中,需要注意线程安全问题。由于ArrayBlockingQueue
通常是多线程环境下使用的,对数组的操作和状态更新都需要进行同步处理。可以使用synchronized
关键字或者ReentrantLock
来保证线程安全。例如,在扩容和缩容方法的关键代码块上加锁:
private final ReentrantLock lock = new ReentrantLock();
public void resize(int newCapacity) {
lock.lock();
try {
// 扩容或缩容逻辑代码
} finally {
lock.unlock();
}
}