持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
新增
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();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
checkNotNull(e); 元素不能为空
while (count == items.length) notFull.await();队列如果是满的,就无限等待, 一直等待队列中有数据被拿走时,自己被唤醒
items[putIndex] = x; putIndex 为本次插入的位置
if (++putIndex == items.length) putIndex = 0; ++ putIndex 计算下次插入的位置,如果下次插入的位置,正好等于队尾,下次插入就从 0 开始
notEmpty.signal();唤醒因为队列空导致的等待线程
数据新增都会按照 putIndex 的位置进行新增,如果队列满,无限阻塞。主要新增分为了两种情况:
- 如果新增的位置居中,则直接新增.
- 如果新增的位置到队尾了,则下次新增时就要从头开始
获取数据
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
final Object[] items = this.items;
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}
while (count == 0) notEmpty.await();如果队列为空,无限等待,直到队列中有数据被 put 后,自己被唤醒
return dequeue();从队列中拿数据
E x = (E) items[takeIndex]; takeIndex 代表本次拿数据的位置,是上一次拿数据时计算好的
items[takeIndex] = null; 帮助 gc
if (++takeIndex == items.length) takeIndex = 0; ++ takeIndex 计算下次拿数据的位置,如果正好等于队尾的话,下次就从 0 开始拿数据
count--; 队列实际大小减 1
notFull.signal(); 唤醒被队列满所阻塞的线程
每次获取数据的位置就是 takeIndex 的位置,在找到本次应该获取的数据之后,会把 takeIndex 加 1,计算下次拿数据时的索引位置,有个特别的情况是:如果本次拿数据的位置已经是队尾了,那么下次拿数据的位置就要从头开始,就是从 0 开始了。