先说点题外话,安利一波看jdk源码的好处,如果你不想看,略过也可
看源码真的对咱们程序员很有帮助。大家可能觉得看源码就是为了精通内部机制、然后会用,或者就是为了面试。但其实不是的,就我自身而言,我刚接触源码,看ArrayList的时候也是非常非常的慢,很多地方不知道大神写的什么意思,他们写这块逻辑的时候是怎么考虑的,可能得边看边存疑,然后回头再看一遍又一遍,又枯燥又没有成就感,哪有自己写一个页面或者一个后台功能来的爽,像极了当年高考做英语阅读的时候,因为看过的东西太少。但当你慢慢地啃下了第一篇源码,脑子里有了一个大致的原理,再看第二篇,你会发现有很多逻辑跟其他源码一样,能看懂大部分,又能学到一些新东西,再看第三篇、第四篇,良性循环,脑子里的知识越来越多,其他的新知识学起来也会更快
虽然第一步很难很难,但是只要你迈出了这一步,后面的道路肯定会越来越自信的
下面我们进入正题,ConcurrrentLinkedQueue是一个支持并发的高性能排队队列,我们以一个简单的例子开始
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>(); queue.offer(""); queue.poll();很简单,在构造函数中初始化了首尾节点
public ConcurrentLinkedQueue() { head = tail = new Node<E>(null); }节点的构造函数就是初始化节点的值
所以ConcurrentLinkedQueue初始化了值为空的首尾节点
private static class Node<E> { volatile E item; volatile Node<E> next; Node(E item) { UNSAFE.putObject(this, itemOffset, item); } boolean casItem(E cmp, E val) { return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } void lazySetNext(Node<E> val) { UNSAFE.putOrderedObject(this, nextOffset, val); } boolean casNext(Node<E> cmp, Node<E> val) { return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } // Unsafe mechanics private static final sun.misc.Unsafe UNSAFE; private static final long itemOffset; private static final long nextOffset; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> k = Node.class; itemOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("item")); nextOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("next")); } catch (Exception e) { throw new Error(e); } } }这个updateHead需要注意一下
他的作用是更新头节点,但包含的操作不仅仅是cas,还有个lasySetNext方法用来将老的头节点的后继指向自己,这样对并发入队出队同一个节点有区分作用
final void updateHead(Node<E> h, Node<E> p) { if (h != p && casHead(h, p)) h.lazySetNext(h); } void lazySetNext(Node<E> val) { UNSAFE.putOrderedObject(this, nextOffset, val); }