20171204_ConcurrentLinkedList源码的一些问题
来源:互联网 发布:董承非 知乎 编辑:程序博客网 时间:2024/05/29 09:30
今天在看ConcurrentLinkedList的1.8源码,结合网上的一些博客,发现有一些地方有坑,不是太好理解,就写下来总结一下吧。
首先,ConcurrentLinkedList是一个并发容器,它的并发性是通过CAS来实现的,这是实现非阻塞并发算法的基础。然后,head/tail 并非总是指向队列的头 / 尾节点,也就是说允许队列处于不一致状态,这个特性把入队 / 出队时,原本需要一起原子化执行的两个步骤分离开来,从而缩小了入队 / 出队时需要原子化更新值的范围到唯一变量,这是非阻塞算法得以实现的关键。另外,下面的两点,很重要,这是理解算法的关键:
head的不变性和可变性条件
不变性:
1. 所有未删除节点,都能从head通过调用succ()方法遍历可达。
2. head不能为null。
3. head节点的next域不能引用到自身。
可变性:
1. head节点的item域可能为null,也可能不为null。
2. 允许tail滞后(lag behind)于head,也就是说:从head开始遍历队列,不一定能到达tail。
tail的不变性和可变性条件
不变性:
1. 通过tail调用succ()方法,最后节点总是可达的。
2. tail不能为null。
可变性:
1. tail节点的item域可能为null,也可能不为 null。
2. 允许tail滞后于head,也就是说:从head开始遍历队列,不一定能到达tail。
3. tail节点的next域可以引用到自身。
下面来看入队操作:
public boolean offer(E e) { checkNotNull(e); final Node<E> newNode = new Node<E>(e); for (Node<E> t = tail, p = t;;) { Node<E> q = p.next; if (q == null) { if (p.casNext(null, newNode)) { if (p != t) //1 casTail(t, newNode); return true; } } else if (p == q) //2 p = (t != (t = tail)) ? t : head; else //3 p = (p != t && t != (t = tail)) ? t : q; } }
在这我捡几个疑惑说:
1. if (p != t) 在单线程中是不需要考虑这个问题的,这个if判断一直为假。那么,在多线程中,这个判断什么时候为真呢?答案是在下面的else if,p可能会赋值为head,是不是会感到奇怪,在2中我会解释。p != t,说明此时tail没有更新,我们用casTail方法进行更新。在这里,casTail即使失败也没有关系,因为这说明有其他线程对tail更新了。
2. if (p == q) 什么情况下会出现,上面我们在tail的可变性上说了,tail.next可以指向自己,这表明tail现在所在指向的结点已被删除(从head遍历无法到达tail),那么就要从head开始遍历到所有的未删除结点。这就是我们要将p赋值为head的原因。
3. 剩下这种情况就是tail不是指向最后一个节点的时候,此时我们将p更新为q。
再来看出队操作:
public E poll(){ restartFromHead: for (;;) { for (Node<E> h = head, p = h, q;;) { E item = p.item; if (item != null && p.casItem(item, null)) { if (p != h) //1 updateHead(h, ((q = p.next) != null) ? q : p); return item; } else if ((q = p.next) == null) //2 { updateHead(h, p); return null; } else if (p == q) //3 continue restartFromHead; else //4 p = q; } } }
看明白上面offer的几个疑点,相信这就不难解释了
1. 这和offer的1很相似,这时我们更新head节点。
2. head的item为null,而它next节点为null的时候,将head指向p这个伪节点,返回null。
3. p == q这个条件有点奇怪,应该是在4时候将q赋值给p,循环后另一个线程将q这个节点给删掉了,此时进入3
4. 在结点出队失败后可以保证下次尝试出队时p不为空(之前q = p.next != null才有可能跳到这一步)
- 20171204_ConcurrentLinkedList源码的一些问题
- tensorflow源码安装的一些问题
- AsyncTask的源码解析及遇到的一些问题
- 一些从源码编译 pytorch的时候的问题
- 安装Hypertable源码过程中遇到的一些问题
- Android源码编译的一些心得与问题。
- android 源码下载过程中遇到的一些问题
- 通过源码安装部署keystone环境遇到的一些问题
- Idea部署Openfire源码时遇到的一些问题
- 虚拟机linux上源码安装两个mysql的一些问题。
- 一些病毒的源码
- 一些OpenSocial的源码
- 一些框架的源码
- android 源码编译遇到得一些问题
- android 源码编译遇到得一些问题
- VS 2008编译notepad源码一些问题
- 共享一些J2ME的源码
- 一些源码搜寻的网站
- nodejs安装总结
- 【Scikit-Learn 中文文档】无监督降维
- 读取.tfrecords格式数据集,进行geture的cnn构建、训练、模型保存
- EM算法的两种理解角度
- 数据库设计
- 20171204_ConcurrentLinkedList源码的一些问题
- 基本计算器
- C++高级编程一些笔记
- linux操作命令
- pandas教程---------数据分组(12/4)
- NameNode元数据持久化
- SpringBoot文件上传下载和多文件上传(图文详解)
- XCTool介绍
- 物理standby database的日常维护