publicbooleanoffer(E e){ checkNotNull(e); final Node<E> newNode = new Node<E>(e); // 循环入队,直到成功 // t 是tail节点 // p 是链表此时此刻的尾结点(也可以理解为入队节点) for (Node<E> t = tail, p = t;;) { // q 是尾结点的 next 节点 Node<E> q = p.next; // 如果q为null,说明到链表尾部了 if (q == null) { // CAS 更新 next为新节点,失败就重试 if (p.casNext(null, newNode)) { // 然后更新tail节点 // 这里采用的是一种巧妙的累计更新,也就是说下个 CAS p才会不等于tail // 相当于循环两次更新一次 tail,所以才有了最下面的两个判断来设置尾结点p if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK. returntrue; } } // p 和 p的next都为空(非对象为空,还是有next的) elseif (p == q) p = (t != (t = tail)) ? t : head; else // 重置 p节点为下一个入队节点(这一步可以理解为校准,入队节点最终永远要指向最新的尾结点) p = (p != t && t != (t = tail)) ? t : q; } }
3.2 出队列操作
3.2.1 poll()
可以看到 poll 的大概代码设计是和 offer 差不多的,这里是把 p 做为 头节点来维护(出队节点),同样是每两次 CAS 更新数据,更新一次头节点
Node<E> first(){ restartFromHead: for (;;) { for (Node<E> h = head, p = h, q;;) { // 获取头节点的值,顺带更新一波 Head boolean hasItem = (p.item != null); if (hasItem || (q = p.next) == null) { updateHead(h, p); return hasItem ? p : null; } elseif (p == q) continue restartFromHead; else p = q; } } }
3.3.2 size()
遍历队列,计数,效率较差
1 2 3 4 5 6 7 8 9
publicintsize(){ int count = 0; for (Node<E> p = first(); p != null; p = succ(p)) if (p.item != null) // Collection.size() spec says to max out if (++count == Integer.MAX_VALUE) break; return count; }
通过对以上的核心方法的分析,回到问题 TOP 1 可以明白基本都是采用 CAS 自旋来实现的,保证了线程的安全性