初始化
创建的时候,实际就是指定了队列大小 capacity,然后队列node其实就是个单向列表结构,item 是当前元素,next 是下一个元素的引用。默认head指针和last指针都是指向这个空节点。ps: 是不是有点责任链模式的感觉呢。
put
put 从队列尾部插入节点
1. 首先也是获取到了 ReentrantLock 锁 ,进行 putLock 加锁。
2. 添加元素之前,先 while (count.get() == capacity) 判断一下,count 是队列中的个数,capacity 是指定的最大个数。相等就说明队列满了,那当前线程就要park挂起释放锁。
3. enqueue(node) 这个是核心,走到这里说明可以添加元素了。 "last = last.next = node;" 这行代码的意思就是先将 last.next 的指针指向node,然后再把 last 也指向它,说明是最后一个元素(初始化的时候我们把 head last 都是指向null这个头节点的嘛,现在头节点还是保持不动)。
4. c 是此次put前的元素个数,然后count自增1了。c + 1 < capacity 说明队列没有满,也尝试去唤醒阻塞的队列 (这种代码确实可读性不太好,像hashmap里面也是大量这样的代码)。
5. c == 0 ,表示之前队列位空,说明可能有线程执行take操作被阻塞住了
take
take 从头部弹出节点
1. 先获取到 ReentrantLock 锁 ,进行 takeLock 加锁。
2. while (count.get() == 0)那就说明队列中没有元素,然后把自己挂起来一直等待(前面不是说了put也会唤醒等待的元素嘛,那个时候就会唤醒这个线程)。
3. dequeue();就是弹出第一个节点
4. c = count.getAndDecrement(); 首先是c = count的,然后 count-1 ,也就是说 c-count=1
5. c > 1,也就是说task之前起码是有2个线程等待的,现在 dequeue() 之后起码还有一条数据,那就去唤醒线程
6. c == capacity,就是说 take 之前满了,那就很可能有人put的时候阻塞了,现在就去尝试唤醒它
。