ConcurrentLinkedQueue源码解析

ConcurrentLinkedQueue

一、 类图

在这里插入图片描述

二、 构造方法

	public class ConcurrentLinkedQueue<E> .... {
			

		// 头节点,
		private transient volatile Node<E> head;
		// 尾节点,尾节点不一定是链表的最后一个节点
		private transient volatile Node<E> tail;
		// 构造
		public ConcurrentLinkedQueue() {
			head = tail = new Node<E>(null);
		}
	......................
	}
  1. 头节点 head 和尾节点 tail 都被 volatile 修饰
  2. 执行构造方法节点变化如下所示:
    在这里插入图片描述

三、 offer方法

  1. 源码

     public boolean offer(E e) {
     	// 判空,为空则抛出空指针异常
     	checkNotNull(e);
     	// 创建要添加的节点
     	final Node<E> newNode = new Node<E>(e);
     	
     	// 无限循环,入队不成功,则反复入队
     	// t 表示 tail 节点
     	// p 表示链表的尾节点,默认等于 tail 节点
     	for (Node<E> t = tail, p = t;;) {
     		// q 为尾节点的下一个节点         
     		Node<E> q = p.next;
     		// 如果尾节点的下一个节点为空,则表示 p 为尾节点
     		if (q == null) {
     			// CAS 设置尾节点的下一个节点为新添加的节点,如果设置失败,在再次尝试
     			if (p.casNext(null, newNode)) {
     				// 如果tail节点有大于等于1个的 next 节点,则更新 tail 节点,将新添加的节点设置为 tail 节点
     				if (p != t) // 相当于循环两次更新一次 tail 节点
     					casTail(t, newNode);  // 新添加的节点设置为tail节点,允许失败,失败了表示有其他线程成功更新了tail节点
     				return true;
     			}
     		}
     		else if (p == q) // 只有在尾节点和尾节点的下一个节点为空的情况下成立
     			p = (t != (t = tail)) ? t : head;
     		else 
     			// 把 tail节点设置为为尾节点,再次循环设置下一个节点
     			p = (p != t && t != (t = tail)) ? t : q;
     	}
     }
    
  2. offer方法做了什么

    1. 新增元素都放到单链的尾部,就做了这么一件事情

3.1、 添加第一个节点

	for (Node<E> t = tail, p = t;;) {
	            Node<E> q = p.next;

在这里插入图片描述

    if (q == null) {
        if (p.casNext(null, newNode)) {

            if (p != t) 
                casTail(t, newNode);  
            return true;
        }
    }

这个时候下一个节点为null,也就是q==null, 执行代码将新添加的节点设置为尾节点的下一个节点。
这个时候p节点和 t节点相等,都指向尾节点,不会更新 tail 节点,设置成功后,链表如下:

在这里插入图片描述

3.2、 添加第二个节点

在这里插入图片描述
if (q == null) {

}
else if (p == q)
// p = q = null
p = (t != (t = tail)) ? t : head;
else
p = (p != t && t != (t = tail)) ? t : q;

创建一个节点,执行for循环,这个是q节点是有内容的(node),所以不会执行q==null的逻辑
又因为p!=q,所以执行 p = (p != t && t != (t = tail)) ? t : q。
这句代码是什么意思呢,很多人看不懂,其实很简单

  1. 就是将p指向尾节点

    1. t != (t = tail):如果t节点和尾节点(tail)不一样了,因为tail是线程可见的。也就是说多线程情况更新tail节点。
    2. p != t:多线程情况更新了p节点:p不是尾节点
    3. (p != t && t != (t = tail)) :如果t还是tail节点或者p节点没有受多线程影响,将p指向t,否则p指向q
  2. 变化如下

在这里插入图片描述
之后,在走到for循环, 执行 Node q = p.next,这里我们知道q=null。 所以,会把新添加的节点设置为 p 的下一个节点:

        if (q == null) {
            if (p.casNext(null, newNode)) {
                if (p != t) 
                    casTail(t, newNode);  
                return true;
            }
        }

又因为p!=t,尝试设置tail指向newNode,最后变化如下

在这里插入图片描述

3.3、 添加第3个元素,添加成功后,链表如下所示:

在这里插入图片描述

3.4、 添加第4个元素,添加成功后,链表如下所示:

在这里插入图片描述

3.5、 重点注意

  1. 上面在添加元素时,每循环两次才会更新一次 tail 节点。
  2. 为什么不让 tail 节点永远为队列的尾节点,如果让 tail 节点永远为队列的尾节点,则实现的代码会更少且逻辑也会更清晰
    1. 这是因为,如果让 tail 永远为队列的尾节点,则每次都需要使用循环CAS更新tail节点,如果能减少更新 tail 节点的次数,入队的性能岂不是更高?所以说并不是每次入队都需要更新尾节点,只有在tail节点和尾节点不想等的情况下才更新,这样能减少更新此时,提高效率。

四、 poll方法(获取元素)

  1. ConcurrentLinkedQueue是一个FIFO的队列,所以获取元素的时候,总是获取到队列的第一个元素;

  2. poll()方法获取元素的时候,返回链表的第一个元素,并删除.

     public E poll() {
     	// 循环跳转,goto语法
     	restartFromHead:
     	for (;;) {
     		// p 表示要出队的节点,默认为 head节点
     		for (Node<E> h = head, p = h, q;;) {
     			// 出队的元素
     			E item = p.item;
     			// 如果出队的元素不为空,则把要出队的元素设置null,不更新head节点;如果出队元素为null或者cas设置失败,则表示有其他线程已经进行修改,则需要重写获取
     			if (item != null && p.casItem(item, null)) {
     				if (p != h) // 当head元素为空,才会更新head节点,这里循环两次,更新一次head节点
     					updateHead(h, ((q = p.next) != null) ? q : p); // 更新head节点
     				return item;
     			}
     			// 队列为空,返回null
     			else if ((q = p.next) == null) {
     				updateHead(h, p);
     				return null;
     			}
     			else if (p == q)
     				continue restartFromHead;
     			// 把 p 的next节点赋值给p
     			else
     				p = q;
     		}
     	}
     }
    

4.1、 首先队列图

在这里插入图片描述

4.2、 获取节点 item0

	for (Node<E> h = head, p = h, q;;) {
	       E item = p.item;
	    ..............................
	}
  1. 标记如下

在这里插入图片描述

因为此时 p.item 不为空,所示 CAS 设置 p.item 为null,走第一个if语句,又因为此时 h 节点的 item 不为空,即 p 和 h 相等,所以不会更新头节点,直接返回 p 节点中的元素 item0,如下图所示:

           if (item != null && p.casItem(item, null)) {
                // 不会执行
                if (p != h) // 
                    updateHead(h, ((q = p.next) != null) ? q : p);
                return item;
            }

在这里插入图片描述

4.2、 接下来再获取 item1,进行标记,如下所示:

在这里插入图片描述
因为 p.item 为空, 不执行 if (item != null && p.casItem(item, null)),之后执行 else if ((q = p.next) == null),此时,q指向了 item1。注意,这里先执行q = p.next,也就是说先让q指向了 item1,再来判断是否为null,实际上下一个节点有值。

      for (Node<E> h = head, p = h, q;;) {
            E item = p.item;

            if (item != null && p.casItem(item, null)) {
             .......
            }
            else if ((q = p.next) == null) {
                updateHead(h, p);
                return null;
            }
            else if (p == q)
                continue restartFromHead;
            else
                p = q;
        }

在这里插入图片描述

又因为 q 不为空,所有不会更新head节点,之后会执行最后一个else语句:p = q,第一次循环结束,开始第二次循环,又进行标记,此时标记如下:

在这里插入图片描述
因为 p.item 不为空(item1),所以走如下代码逻辑,通过 CAS 把 p.item 设置为null

            if (item != null && p.casItem(item, null)) {
                if (p != h) // 设置下一节点item2为头结点
                    updateHead(h, ((q = p.next) != null) ? q : p);
                return item;
            }

		-------
	    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);
        }

因为此时,head节点元素为空,即 p 和 h 节点不相等,所以会更新头节点,又因为 p.next 即 item2 不为空,所以把 p.next 即 item2 设置为 head节点,设置成功后,队列如下所示:
在这里插入图片描述

4.3、 获取item2也是同样的逻辑

在这里插入图片描述

4.4、 获取item3也是同样的逻辑

在这里插入图片描述

4.5、 重要

  1. 从上可知,head节点不一定就是队列的第一个含有元素的节点,也不是每次获取元素后就更新head节点,只有当head中的元素为空的时候才更新head节点,这和添加 offer() 方法中更新tail节点类似,减少 CAS 更新head节点的次数,出队的效率会更高。

五、 验证

	package com.feizhou.example.demo;
	
	import sun.misc.Unsafe;
	
	import java.lang.reflect.Field;
	import java.util.AbstractQueue;
	import java.util.Iterator;
	import java.util.Queue;
	import java.util.Spliterator;
	import java.util.concurrent.ConcurrentLinkedQueue;
	import java.util.function.Consumer;
	import java.util.function.Predicate;
	import java.util.stream.Stream;
	
	import static java.lang.Thread.currentThread;
	
	public class ConcurrentLinkedQueue2<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable {
	
	    private static final long serialVersionUID = 196745693267521676L;
	    private transient volatile Node<E> head;
	
	    private transient volatile Node<E> tail;
	
	    private static sun.misc.Unsafe UNSAFE;
	    private static final long headOffset;
	    private static final long tailOffset;
	
	    static {
	        try {
	            Field unsafeField = Unsafe.class.getDeclaredFields()[0];
	            unsafeField.setAccessible(true);
	            try {
	                UNSAFE = (Unsafe) unsafeField.get(null);
	            } catch (IllegalAccessException e) {
	                e.printStackTrace();
	            }
	            Class<?> k = ConcurrentLinkedQueue.class;
	            headOffset = UNSAFE.objectFieldOffset
	                    (k.getDeclaredField("head"));
	            tailOffset = UNSAFE.objectFieldOffset
	                    (k.getDeclaredField("tail"));
	        } catch (Exception e) {
	            throw new Error(e);
	        }
	    }
	
	
	    // 构造
	    public ConcurrentLinkedQueue2() {
	        head = tail = new Node<E>(null);
	        System.out.println("ConcurrentLinkedQueue2----: 创建头结点和为节点" + head);
	    }
	
	
	    final void updateHead(Node<E> h, Node<E> p) {
	        System.out.println("updateHead--------------p :" + p);
	        System.out.println("updateHead--------------h :" + h);
	
	        if (h != p && casHead(h, p)) {
	            System.out.println("updateHead---casHead-->h:" + UNSAFE.getObjectVolatile(this, headOffset));
	        }
	
	    }
	
	
	    @Override
	    public boolean offer(E e) {
	        System.out.println("offer---- : 非空校验");
	        checkNotNull(e);
	        System.out.println("offer---- : 创建要添加的节点");
	        final Node<E> newNode = new Node<E>(e);
	
	        // 无限循环,入队不成功,则反复入队
	        // t 表示 tail 节点
	        // p 表示链表的尾节点,默认等于 tail 节点
	        for (Node<E> t = tail, p = t; ; ) {
	            //循环下一个节点
	            Node<E> q = p.next;
	
	            System.out.println("offer---- t:" + t);
	            System.out.println("offer---- p:" + p);
	            System.out.println("offer---- q:" + q);
	            // 如果尾节点的下一个节点为空,则表示 p 为尾节点
	            if (q == null) {
	                // CAS 设置尾节点的下一个节点为新添加的节点,如果设置失败,在再次尝试
	                if (p.casNext(null, newNode)) {
	                    System.out.println("offer---- casNext: true:");
	                    // 如果tail节点有大于等于1个的 next 节点,则更新 tail 节点,将新添加的节点设置为 tail 节点
	                    if (p != t) {// 相当于循环两次更新一次 tail 节点
	                        System.out.println("offer---- casTail: " + casTail(t, newNode));
	                    }
	                    System.out.println("offer---- 结束return true");
	                    return true;
	                }
	            } else if (p == q) {// 只有在尾节点和尾节点的下一个节点为空的情况下成立
	                //多线程操作时候,由于poll操作移除元素后有可能会把head变为自引用,然后head的next变为新head,所以这里需要
	                //重新找新的head,因为新的head后面的节点才是正常的节点。
	                System.out.println("offer---- p == q)--》  tail:" + tail);
	                System.out.println("offer---- p == q)--》     t:" + t);
	                System.out.println("offer---- p == q--》      p:" + p);
	                System.out.println("offer---- p == q--》      (t = tail):" + (t = tail));
	                p = (t != (t = tail)) ? t : head;
	
	            } else {
	                // 把 tail节点设置为为尾节点,再次循环设置下一个节点
	                System.out.println("offer---- p != q--》  tail:" + tail);
	                System.out.println("offer---- p != q--》     t:" + t);
	                System.out.println("offer---- p != q--》     p:" + p);
	                System.out.println("offer---- p != q--》     p != t:" + (p != t) + "----(t != (t = tail)): " + (t != (t = tail)));
	                p = (p != t && t != (t = tail)) ? t : q;
	
	            }
	
	
	        }
	    }
	
	    @Override
	    public E poll() {
	        restartFromHead:
	        for (; ; ) {
	            for (Node<E> h = head, p = h, q; ; ) {
	                E item = p.item;
	                System.out.println("poll------- p:" + p);
	                System.out.println("poll------- h:" + h);
	                System.out.println("poll---- item:" + item);
	
	                if (item != null && p.casItem(item, null)) {
	                    System.out.println("poll---- item != null && p.casItem(item, null)-->    p :" + p);
	                    System.out.println("poll---- item != null && p.casItem(item, null)-->    h :" + h);
	                    if (p != h) {
	                        updateHead(h, ((q = p.next) != null) ? q : p);
	                    }
	
	                    return item;
	                } else if ((q = p.next) == null) {
	                    System.out.println("poll---- ((q = p.next) == null)--> q :" + q);
	                    System.out.println("poll---- ((q = p.next) == null)--> p :" + p);
	                    updateHead(h, p);
	                    return null;
	                } else if (p == q) {
	                    System.out.println("poll---- (p == q)--> q :" + q);
	                    continue restartFromHead;
	                } else {
	                    System.out.println("poll------- else --> q :" + q);
	                    p = q;
	                    System.out.println("poll------- else --> p :" + p);
	                }
	
	            }
	        }
	    }
	
	
	    private static void checkNotNull(Object v) {
	        if (v == null) {
	            throw new NullPointerException();
	        }
	
	    }
	
	    private boolean casTail(Node<E> cmp, Node<E> val) {
	        return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
	    }
	
	    private boolean casHead(Node<E> cmp, Node<E> val) {
	        return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
	    }
	
	
	    @Override
	    public E peek() {
	        return null;
	    }
	
	    @Override
	    public Iterator<E> iterator() {
	        return null;
	    }
	
	    @Override
	    public void forEach(Consumer<? super E> action) {
	
	    }
	
	    @Override
	    public boolean removeIf(Predicate<? super E> filter) {
	        return false;
	    }
	
	    @Override
	    public Spliterator<E> spliterator() {
	        return null;
	    }
	
	    @Override
	    public Stream<E> stream() {
	        return null;
	    }
	
	    @Override
	    public Stream<E> parallelStream() {
	        return null;
	    }
	
	    @Override
	    public int size() {
	        return 0;
	    }
	
	
	    private static class Node<E> {
	        volatile E item;
	        volatile Node<E> next;
	        private static sun.misc.Unsafe UNSAFE;
	        private static final long itemOffset;
	        private static final long nextOffset;
	
	        static {
	            try {
	                Field unsafeField = Unsafe.class.getDeclaredFields()[0];
	                unsafeField.setAccessible(true);
	                try {
	                    UNSAFE = (Unsafe) unsafeField.get(null);
	                } catch (IllegalAccessException e) {
	                    e.printStackTrace();
	                }
	                Class<?> k = Node.class;
	                itemOffset = UNSAFE.objectFieldOffset
	                        (k.getDeclaredField("item"));
	                nextOffset = UNSAFE.objectFieldOffset
	                        (k.getDeclaredField("next"));
	            } catch (Exception e) {
	                throw new Error(e);
	            }
	        }
	
	        Node(E item) {
	            UNSAFE.putObject(this, itemOffset, item);
	
	        }
	
	        boolean casItem(E cmp, E val) {
	
	            return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
	        }
	
	
	
	        boolean casNext(Node<E> cmp, Node<E> val) {
	            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
	        }
	
	        @Override
	        public String toString() {
	            return "Node{" +
	                    "item=" + item +
	                    ", next=" + next +
	                    '}';
	        }
	
	    }
	
	
	    public static void main(String[] args) throws InterruptedException {
	
	        System.out.println("main---:当前线程是=" + currentThread());
	        Thread.sleep(200L);
	        System.out.println("main休息200毫秒");
	
	        ConcurrentLinkedQueue2<String> list = new ConcurrentLinkedQueue2<String>();
	        //        ConcurrentLinkedQueue<String> list = new ConcurrentLinkedQueue<String>();
	        Runnable run = new Runnable() {
	            @Override
	            public void run() {
	
	                for (int i = 0; i < 5; i++) {
	                    System.out.println("offer 元素 :item" + i);
	                    list.offer("item" + i);
	                }
	
	            }
	        };
	
	        Runnable run2 = new Runnable() {
	            @Override
	            public void run() {
	                for (int i = 0; i < 5; i++) {
	                    System.out.println("poll 元素 :" + list.poll());
	                }
	            }
	        };
	
	
	        Thread t1 = new Thread(run);
	        Thread t2 = new Thread(run2);
	
	        t1.setName("线程1");
	        t2.setName("线程2");
	
	
	        t1.start();
	        Thread.sleep(1000L);
	        System.out.println("main休息1000毫秒");
	        t2.start();
	        Thread.sleep(200L);
	        //        System.out.println("main休息200毫秒");
	        //        System.out.println("删除 元素 :" + list.remove("offer3"));
	    }
	
	}

测试结果

	main---:当前线程是=Thread[main,5,main]
	main休息200毫秒
	ConcurrentLinkedQueue2----: 创建头结点和为节点Node{item=null, next=null}
	offer 元素 :item0
	offer---- : 非空校验
	offer---- : 创建要添加的节点
	offer---- t:Node{item=null, next=null}
	offer---- p:Node{item=null, next=null}
	offer---- q:null
	offer---- casNext: true:
	offer---- 结束return true
	offer 元素 :item1
	offer---- : 非空校验
	offer---- : 创建要添加的节点
	offer---- t:Node{item=null, next=Node{item=item0, next=null}}
	offer---- p:Node{item=null, next=Node{item=item0, next=null}}
	offer---- q:Node{item=item0, next=null}
	offer---- p != q--》  tail:Node{item=null, next=Node{item=item0, next=null}}
	offer---- p != q--》     t:Node{item=null, next=Node{item=item0, next=null}}
	offer---- p != q--》     p:Node{item=null, next=Node{item=item0, next=null}}
	offer---- p != q--》     p != t:false----(t != (t = tail)): false
	offer---- t:Node{item=null, next=Node{item=item0, next=null}}
	offer---- p:Node{item=item0, next=null}
	offer---- q:null
	offer---- casNext: true:
	offer---- casTail: true
	offer---- 结束return true
	offer 元素 :item2
	offer---- : 非空校验
	offer---- : 创建要添加的节点
	offer---- t:Node{item=item1, next=null}
	offer---- p:Node{item=item1, next=null}
	offer---- q:null
	offer---- casNext: true:
	offer---- 结束return true
	offer 元素 :item3
	offer---- : 非空校验
	offer---- : 创建要添加的节点
	offer---- t:Node{item=item1, next=Node{item=item2, next=null}}
	offer---- p:Node{item=item1, next=Node{item=item2, next=null}}
	offer---- q:Node{item=item2, next=null}
	offer---- p != q--》  tail:Node{item=item1, next=Node{item=item2, next=null}}
	offer---- p != q--》     t:Node{item=item1, next=Node{item=item2, next=null}}
	offer---- p != q--》     p:Node{item=item1, next=Node{item=item2, next=null}}
	offer---- p != q--》     p != t:false----(t != (t = tail)): false
	offer---- t:Node{item=item1, next=Node{item=item2, next=null}}
	offer---- p:Node{item=item2, next=null}
	offer---- q:null
	offer---- casNext: true:
	offer---- casTail: true
	offer---- 结束return true
	offer 元素 :item4
	offer---- : 非空校验
	offer---- : 创建要添加的节点
	offer---- t:Node{item=item3, next=null}
	offer---- p:Node{item=item3, next=null}
	offer---- q:null
	offer---- casNext: true:
	offer---- 结束return true
	main休息1000毫秒
	poll------- p:Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
	poll------- h:Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
	poll---- item:null
	poll------- else --> q :Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}
	poll------- else --> p :Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
	poll------- p:Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}
	poll------- h:Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
	poll---- item:item0
	poll---- item != null && p.casItem(item, null)-->    p :Node{item=null, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}
	poll---- item != null && p.casItem(item, null)-->    h :Node{item=null, next=Node{item=null, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
	updateHead--------------p :Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	updateHead--------------h :Node{item=null, next=Node{item=null, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
	updateHead---casHead-->h:Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll 元素 :item0
	poll------- p:Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll------- h:Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll---- item:item1
	poll---- item != null && p.casItem(item, null)-->    p :Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll---- item != null && p.casItem(item, null)-->    h :Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll 元素 :item1
	poll------- p:Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll------- h:Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll---- item:null
	poll------- else --> q :Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}
	poll------- else --> p :Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll------- p:Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}
	poll------- h:Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	poll---- item:item2
	poll---- item != null && p.casItem(item, null)-->    p :Node{item=null, next=Node{item=item3, next=Node{item=item4, next=null}}}
	poll---- item != null && p.casItem(item, null)-->    h :Node{item=null, next=Node{item=null, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	updateHead--------------p :Node{item=item3, next=Node{item=item4, next=null}}
	updateHead--------------h :Node{item=null, next=Node{item=null, next=Node{item=item3, next=Node{item=item4, next=null}}}}
	updateHead---casHead-->h:Node{item=item3, next=Node{item=item4, next=null}}
	poll 元素 :item2
	poll------- p:Node{item=item3, next=Node{item=item4, next=null}}
	poll------- h:Node{item=item3, next=Node{item=item4, next=null}}
	poll---- item:item3
	poll---- item != null && p.casItem(item, null)-->    p :Node{item=null, next=Node{item=item4, next=null}}
	poll---- item != null && p.casItem(item, null)-->    h :Node{item=null, next=Node{item=item4, next=null}}
	poll 元素 :item3
	poll------- p:Node{item=null, next=Node{item=item4, next=null}}
	poll------- h:Node{item=null, next=Node{item=item4, next=null}}
	poll---- item:null
	poll------- else --> q :Node{item=item4, next=null}
	poll------- else --> p :Node{item=null, next=Node{item=item4, next=null}}
	poll------- p:Node{item=item4, next=null}
	poll------- h:Node{item=null, next=Node{item=item4, next=null}}
	poll---- item:item4
	poll---- item != null && p.casItem(item, null)-->    p :Node{item=null, next=null}
	poll---- item != null && p.casItem(item, null)-->    h :Node{item=null, next=Node{item=null, next=null}}
	updateHead--------------p :Node{item=null, next=null}
	updateHead--------------h :Node{item=null, next=Node{item=null, next=null}}
	updateHead---casHead-->h:Node{item=null, next=null}
	poll 元素 :item4
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值