LinkedBlockingQueue源码解析(3)
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
4.3、public E take() throws InterruptedException
原理:
将队头元素出队,如果队列空了,一直阻塞,直到队列不为空或者线程被中断
使用方法:
try {
abq.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
源代码:
/**
* 出队:
* 如果队列空了,一直阻塞,直到队列不为空或者线程被中断
*/
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;//获取队列中的元素总量
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();//获取出队锁
try {
while (count.get() == 0) {//如果没有元素,一直阻塞
/*
* 加入等待队列, 一直等待条件notEmpty(即被其他线程唤醒)
* (唤醒其实就是,有线程将一个元素入队了,然后调用notEmpty.signal()唤醒其他等待这个条件的线程,同时队列也不空了)
*/
notEmpty.await();
}
x = dequeue();//出队
c = count.getAndDecrement();//元素数量-1
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
总结:
1、具体入队与出队的原理图:
图中每一个节点前半部分表示封装的数据x,后边的表示指向的下一个引用。
1.1、初始化

初始化之后,初始化一个数据为null,且head和last节点都是这个节点。
1.2、入队两个元素过后

这个可以根据入队方法enqueue(E x)来看,源代码再贴一遍:
/**
* 创建一个节点,并加入链表尾部
*
* @param x
*/
private void enqueue(E x) {
/*
* 封装新节点,并赋给当前的最后一个节点的下一个节点,然后在将这个节点设为最后一个节点
*/
last = last.next = new Node<E>(x);
}
其实这我们就可以发现其实真正意义上出队的头节点是Head节点的下一个节点。(这也就是Node这个内部类中对next的注释,我没有翻译)
1.3、出队一个元素后

表面上看,只是将头节点的next指针指向了要删除的x1.next,事实上这样我觉的就完全可以,但是jdk实际上是将原来的head节点删除了,而上边看到的这个head节点,正是刚刚出队的x1节点,只是其值被置空了。
这一块对应着源代码来看:dequeue()
/**
* 从队列头部移除一个节点
*/
private E dequeue() {
Node<E> h = head;// 获取头节点:x==null
Node<E> first = h.next;// 将头节点的下一个节点赋值给first
h.next = h; // 将当前将要出队的节点置null(为了使其做head节点做准备)
head = first;// 将当前将要出队的节点作为了头节点
E x = first.item;// 获取出队节点的值
first.item = null;// 将出队节点的值置空
return x;
}
2、三种入队对比:
offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false-->不阻塞
put(E e):如果队列满了,一直阻塞,直到队列不满了或者线程被中断-->阻塞
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:-->阻塞
被唤醒
等待时间超时
当前线程被中断
3、三种出队对比:
poll():如果没有元素,直接返回null;如果有元素,出队
take():如果队列空了,一直阻塞,直到队列不为空或者线程被中断-->阻塞
poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
被唤醒
等待时间超时
当前线程被中断
4、ArrayBlockingQueue与LinkedBlockingQueue对比
ArrayBlockingQueue:
一个对象数组+一把锁+两个条件
入队与出队都用同一把锁
在只有入队高并发或出队高并发的情况下,因为操作数组,且不需要扩容,性能很高
采用了数组,必须指定大小,即容量有限
LinkedBlockingQueue:
一个单向链表+两把锁+两个条件
两把锁,一把用于入队,一把用于出队,有效的避免了入队与出队时使用一把锁带来的竞争。
在入队与出队都高并发的情况下,性能比ArrayBlockingQueue高很多
采用了链表,最大容量为整数最大值,可看做容量无限
两个疑问:
入队时:c==0是怎样出现的?
出队时:c==capcity是怎样出现的?
这两个疑问,都是基于对于AtomicInteger的不熟,不明白LinkedBlockingQueue引用的这两个方法(getAndIncrement和getAndDecrement)先返回旧值还是新值,关于AtomicInteger的源码介绍,请查看《第十一章 AtomicInteger源码解析》,具体链接如下:
http://www.cnblogs.com/java-zhao/p/5140158.html
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 wireshark抓包分析——TCP/IP协议
【推荐】 漫画解读“跨视图粒度计算”,了解有数分析利器
【推荐】 Docker容器的原理与实践(上)
LinkedBlockingQueue源码解析(3)的更多相关文章
- LinkedBlockingQueue源码解析
上一篇博客,我们介绍了ArrayBlockQueue,知道了它是基于数组实现的有界阻塞队列,既然有基于数组实现的,那么一定有基于链表实现的队列了,没错,当然有,这就是我们今天的主角:LinkedBlo ...
- 第九章 LinkedBlockingQueue源码解析
1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...
- Java并发包源码学习系列:阻塞队列实现之LinkedBlockingQueue源码解析
目录 LinkedBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e ...
- LinkedBlockingQueue源码解析(1)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2 ...
- LinkedBlockingQueue源码解析(2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3.3.public void put(E e) throws InterruptedException 原 ...
- Java并发包源码学习系列:阻塞队列实现之PriorityBlockingQueue源码解析
目录 PriorityBlockingQueue概述 类图结构及重要字段 什么是二叉堆 堆的基本操作 向上调整void up(int u) 向下调整void down(int u) 构造器 扩容方法t ...
- Java并发包源码学习系列:阻塞队列实现之DelayQueue源码解析
目录 DelayQueue概述 类图及重要字段 Delayed接口 Delayed元素案例 构造器 put take first = null 有什么用 总结 参考阅读 系列传送门: Java并发包源 ...
- Java并发包源码学习系列:阻塞队列实现之SynchronousQueue源码解析
目录 SynchronousQueue概述 使用案例 类图结构 put与take方法 void put(E e) E take() Transfer 公平模式TransferQueue QNode t ...
- Java并发包源码学习系列:阻塞队列实现之LinkedTransferQueue源码解析
目录 LinkedTransferQueue概述 TransferQueue 类图结构及重要字段 Node节点 前置:xfer方法的定义 队列操作三大类 插入元素put.add.offer 获取元素t ...
随机推荐
- [leetcode]277. Find the Celebrity谁是名人
Suppose you are at a party with n people (labeled from 0 to n - 1) and among them, there may exist o ...
- struts框架值栈问题七之EL表达式也会获取到值栈中的数据
7. 问题七:为什么EL也能访问值栈中的数据? * StrutsPreparedAndExecuteFilter的doFilter代码中 request = prepare.wrapRequest(r ...
- PAT 1077 互评成绩计算(20)(代码+思路)
1077 互评成绩计算(20 分) 在浙大的计算机专业课中,经常有互评分组报告这个环节.一个组上台介绍自己的工作,其他组在台下为其表现评分.最后这个组的互评成绩是这样计算的:所有其他组的评分中,去掉一 ...
- web服务器部署过程记录
由于之前没有服务器部署经验,又选择了所有软件都是单独编译安装,遇到很多问题,解决之后还是学习到了很多新东西. 如今回过头来还是选择lnmp集成环境的部署方式比较方便快捷:https://lnmp.or ...
- 一定要 先删除 sc表 中的 某元组 行,,, 再删除 course表中的 元组行
一定要 先删除 sc表 中的 某元组 行,,, 再删除 course表中的 元组行 course表 SC表 删除 course表中的 元组行,,出现错误 sc ---->参 ...
- 还不好好读书吗?清华3D录取通知书出炉,还能动!
近日,清华大学2018录取通知书“亮相”!看完后,网友直呼:哪里可以买到? 打开录取通知书 3D“二校门”跃然纸上 由清华师生共同打造.手工定制.独一无二的2018新版录取通知书来了!在新版录取通知书 ...
- filter 死循环(tomcat 启动完成 ,自动执行filter.dofilter,导致tomcat 启动超时) , tomcat 启动和 servers 启动 不同
package com.diancai.interceptor; import java.io.IOException; import javax.servlet.Filter; import jav ...
- system v消息队列demo(未编译)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> ...
- 构造函数constructor 与析构函数destructor(五)
我们知道当调用默认拷贝构造函数时,一个对象对另一个对象初始化时,这时的赋值时逐成员赋值.这就是浅拷贝,当成员变量有指针时,浅拷贝就会在析构函数那里出现问题.例如下面的例子: //test.h #ifn ...
- centos7 jenkins安装和使用
jenkins 安装和使用 1.先安装jdK1.8 和 maven 此步骤省略 2.进入jenkisn 官网 下载https://jenkins.io/download/ Long-term Supp ...