目录
conditon.await()和condition.signal()
生产者通过put生产元素:blockingQueue.put()
消费者通过take消费元素: blockingQueue.take()
基本认识:
- 生产者去生产和消费者去消费都需要拿到此锁,拿不到就进入lock的AQS队列:ReerantLock lock
- 消费者去消费的条件队列:Condition notEmpty
- 生产者继续生产的条件队列:Condition notFull
- putIndex(每一轮都是从0-array.length,走到array.length后,继续从0开始下一轮):下一个生产者生产的位置
- takeIndex(每一轮都是从0-array.length,走到array.length后,继续从0开始下一轮):下一个消费者消费的位置
- count:当前数组阻塞队列里元素的个数(由于生产者不断生产和消费者不断消费,所以大部分是不连续的元素)
- items:数组阻塞队列
conditon.await()和condition.signal()
ReetnrantLock lock
condition=lock.newCondition
condition.await(每个condtion内部都有自己的一个队列):
- 会释放lock锁
- 将当前线程从AQS队列移除,添加到条件队列contion尾部
- 调用Locksupport.park()进行阻塞(阻塞在await方法内部)
condition.signal()
- 唤醒condition队列的头结点 添加到AQS队列,
- 解除阻塞Locksupport.unpark()
- 此时执行await方法里的自旋获取锁的方法,获取成功后,从conditon队列移除,await执行完毕。原线程接着从await的下一行代码开始执行,实现了唤醒。
整体流程
生产者生产和消费者消费都是在加锁基础上进行的,所以线程安全:
- 生产者从【0-数组长度】循环生产元素,成功生产一个就唤醒消费者来消费,数组被占满后调用await阻塞(此时释放锁,从AQS队列移除,添加到notFull队列尾部),等待着消费者唤醒自己继续生产
- 消费者从【0-数组长度】循环消费元素,成功消费一个就唤醒生产者可以生产了,数组没有元素时,消费者调用await阻塞自己(此时释放锁,从AQS队列移除,添加到notEmpty队列尾部)),等待着生产者唤醒自己继续消费。
- 唤醒过程:从条件队列移除,重新进入AQS队列,自旋获取锁,获取锁成功后继续生产或消费
生产者通过put生产元素:blockingQueue.put()
- 首先需要获取到锁lock.lock()
- 获取锁后判断当前阻塞队列是否已满:count == items.length
- 可以生产:把元素放到items[putIndex]=put的元素,队列元素个数+1,下一个应放的位置+1 putIndex++,如果putIndex+1后=数组长度,则重新置putIndex=0,成功生产一个最后需要唤醒消费者:notEmpty.signal(),之所以唤醒消费者是因为此时队列里至少有一个元素了不满足isEmpty,符合NotEmpty,唤醒NotEmpty队列的第一个节点,将其移动到AQS队列,解除阻塞,重新获取锁,获取成功后,从notEmpty队列移除,继续await去消费
- 不可以生产:调用notFull.await()【此时已满,不满足不满notFull,所以notFull要等待,notFull.await()】调用此方法后会释放锁而从AQS队列移除,此时被添加到条件队列notFull尾部,然后调用LockSupport.park进行阻塞等待着消费者成功消费一个后唤醒自己,此时消费者会调用notFull.signal()唤醒notFull队列的第一个节点,进入可以生产
public void put(E e) throws InterruptedException {
//检查元素是否为null,如果是,抛出NullPointerException
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();
}
消费者通过take消费元素: blockingQueue.take()
- 首先需要获取到锁lock.lock()
- 获取锁后判断当前阻塞队列是否有元素可以消费:count == 0
- 可以消费:items[takeIndex]元素返回给消费者,并且items[takeIndex]置为null,队列元素个数-1,takeIndex+1,同理takeIndex达到数组长度后,同样重新从0开始加到数组长度,成功消费一个后(消费了一个现在肯定不满了,生产者可以继续生产了),调用notFull.signal()唤醒生产者notFull队列的第一个节点firstWaiter,将firstWaiter重新添加到AQS队列,解除阻塞LockSupport.unPark,自选获取锁,获取成功后,从条件队列删除,继续从await那里继续向下生产
- 没有可消费的:调用notEmpty.await()阻塞,释放锁,从AQS队列移除,添加到notEmpty队列尾部,等待生产者成功生产一个后调用notEmpty.signal唤醒自己,然后进入可以消费的步骤。
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; //首先加锁 lock.lockInterruptibly(); try { //如果队列为空,阻塞 while (count == 0) notEmpty.await(); //队列不为空,调用dequeue()出队 return dequeue(); } finally { //释放锁 lock.unlock(); } } private E dequeue() { // assert lock.getHoldCount() == 1; // assert items[takeIndex] != null; final Object[] items = this.items; @SuppressWarnings("unchecked") //取走数据 E x = (E) items[takeIndex]; //置为null,以助gc items[takeIndex] = null; //循环取 if (++takeIndex == items.length) takeIndex = 0; count--; if (itrs != null) itrs.elementDequeued(); //通知因队列满而阻塞的线程 notFull.signal(); return x; }