傻大方


首页 > 潮·科技 > >

阻塞队列(3)LinkedBlockingQueue源码详解( 三 )



按关键词阅读:


null 值在这里通常用于作为特殊值返回 , 比如 poll() 返回 null , 则代表 poll 失败 。
所以 , 如果允许插入 null 值的话 , 那获取的时候 , 就不能很好地用 null 来判断到底是代表失败 , 还是获取的值就是 null 值 。
enqueue 入队/** * Links node at end of queue. * * @param node the node */private void enqueue(Node node) {// assert putLock.isHeldByCurrentThread();// assert last.next == null;last = last.next = node;}这个方法写的非常的精炼 。
建议看不懂的同学可以分成 2 步来看:
last.next = node;last = last.next;将 node 节点放在队列的最后 , 将 last.next 变为新的队尾 。
put 放入元素我们来重点看一下 put() 方法 。
/** * Inserts the specified element at the tail of this queue, waiting if * necessary for space to become available. * * @throws InterruptedException {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @author 老马啸西风 */public void put(E e) throws InterruptedException {if (e == null) throw new NullPointerException();// Note: convention in all put/take/etc is to preset local var// holding count negative to indicate failure unless set.int c = -1;// 创建节点Node node = new Node(e);// 并发控制final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;putLock.lockInterruptibly();try {/** Note that count is used in wait guard even though it is* not protected by lock. This works because count can* only decrease at this point (all other puts are shut* out by lock), and we (or some other waiting put) are* signalled if it ever changes from capacity. Similarly* for all other uses of count in other wait guards.* 如果队列已经满了 , 则进入等待 。*/while (count.get() == capacity) {notFull.await();}// 执行入队enqueue(node);c = count.getAndIncrement();// 这里挺有趣的 , 如果判断未满 , 则会唤醒 notFullif (c + 1 < capacity)notFull.signal();} finally {putLock.unlock();}// c == 0,说明的是刚才新增成功了 。 因为默认是-1if (c == 0)signalNotEmpty();}signalNotEmpty这个方法是通知 notEmpty , 便于元素可以取出 。
/** * Signals a waiting take. Called only from put/offer (which do not * otherwise ordinarily lock takeLock.) */private void signalNotEmpty() {final ReentrantLock takeLock = this.takeLock;takeLock.lock();try {notEmpty.signal();} finally {takeLock.unlock();}}当然这里的对于 notEmpty 的唤醒 , 是通过 takeLock 保证并发安全的 。
这样看来 , LinkedBlockingQuue 和 ArrayBlockingQueue 实现上还是有很大差异的 。
take 获取元素获取元素的实现如下:
/**** 获取一个元素** @author 老马啸西风*/public E take() throws InterruptedException {E x;// 默认的 c = -1;int c = -1;// 并发控制 。// 注意 , 这里的读写锁是分离的 。 这个和 ArrayBlockingList 是不同的 。final AtomicInteger count = this.count;final ReentrantLock takeLock = this.takeLock;takeLock.lockInterruptibly();try {// 如果元素为空 , 则陷入等待 。while (count.get() == 0) {notEmpty.await();}// 执行出队x = dequeue();c = count.getAndDecrement();// 如果总数 > 1 , 则说明不再为空 。// 唤醒 notEmpty 等待的线程if (c > 1)notEmpty.signal();} finally {takeLock.unlock();}// 如果总数等于容量 , 唤醒 notFull// 感觉这个唤醒微妙 , 因为 capacity 默认是 Integer#MAX_VALUE 最大值if (c == capacity)signalNotFull();return x;}dequeue 出队方法/** * Removes a node from head of queue. * 从队首移除元素 * * @author 老马啸西风 * @return the node */private E dequeue() {// assert takeLock.isHeldByCurrentThread();// assert head.item == null;Node h = head;// first 节点变为原来 head.next 节点Node first = h.next;h.next = h; // help GC// 更新 head 节点head = first;// 记录出队的元素E x = first.item;first.item = null;return x;}signalNotFull 唤醒 notFull/** * Signals a waiting put. Called only from take/poll. * 唤醒所有等待放入的线程 。*/private void signalNotFull() {final ReentrantLock putLock = this.putLock;putLock.lock();try {notFull.signal();} finally {putLock.unlock();}}小结本文从 LinkedBlockingQueue 的入门使用 , 逐渐深入到源码分析 。
实际上原理都是类似的 , 不过这个对比 ArrayBlockingQueeu 的锁粒度更加细致 。
没读源码之前 , 我一直以为二者只是链表和数组的区别 。
很多事情 , 都不是我们以为的那个样子 。
希望本文对你有帮助 , 如果有其他想法的话 , 也可以评论区和大家分享哦 。
【阻塞队列(3)LinkedBlockingQueue源码详解】各位极客的点赞收藏转发 , 是老马持续写作的最大动力!
阻塞队列(3)LinkedBlockingQueue源码详解文章插图


稿源:(未知)

【傻大方】网址:http://www.shadafang.com/c/111J2HT2020.html

标题:阻塞队列(3)LinkedBlockingQueue源码详解( 三 )


上一篇:冬季用车小指南,让你的新能源汽车和电池焦虑说拜拜

下一篇:点点滴滴学5G—一文掌握BWP RRC配置信息