Java多线程17:ConcurrentLinkedQueue的详细介绍及源码分析——学习方腾飞Java并发编程的艺术

ConcurrentLinkedQueue

ConcurrentLinkedQueue
1、线程安全:通过volatile来实现多线程对竞争资源的互斥访问
2、无界队列:无大小
3、链表实现:内部维持着一个链表
4、FIFO,先进先出:元素都是从尾部插入到链表,从头部开始返回。

介绍

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael & Scott算法上进行了一些修改。

ConcurrentLinkedQueue的源码

还是从增删等角度看代码

1、构造函数

//head 和 tail 全为volatile修饰的Node,一线程对一个volatile变量的读,总是
//能看到(任意线程)对这个volatile变量最后的写入”
transient volatile Node<E> head;
private transient volatile Node<E> tail;

// 维持一个链表
public ConcurrentLinkedQueue() {
        head = tail = new Node<E>();
    }

// 维持一个链表且传入初始值
public ConcurrentLinkedQueue(Collection<? extends E> c) {
        Node<E> h = null, t = null;
        for (E e : c) {
        // 加入非空元素
            Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
            if (h == null)
                h = t = newNode;
            else
                t.appendRelaxed(t = newNode);
        }
        // 全为null时,
        if (h == null)
            h = t = new Node<E>();
        head = h;
        tail = t;
    }

2、添加add(E e)

public boolean add(E e) {
        return offer(e);//调用offer
    }

        //判断是否为空
        final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));

        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            // tail节点的下一个节点为null
            if (q == null) {
                // p is last node p为尾节点
                if (NEXT.compareAndSet(p, null, newNode)) {
                //对 p 进行 cas 操作, newNode -> p.next

                    if (p != t) // hop two nodes at a time; failure is OK
                        TAIL.weakCompareAndSet(this, t, newNode);
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                /**根据tail的可变性条件和滞后更新策略,我们知道tail的next
                域果指向自己的话,则表明tail现在所在指向的结点已被删除(从head
                遍历无法到达tail),那么就要从head开始遍历到所有的未删除结点
                (这也是上文head的不变性条件保证的),当然,我们还是要判断其他线程
                没有引用到自身了,就可以直接重新尝试插入了。即虽然tail有滞后更新
                当tail为最后一个结点才行  */
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

3、删除poll()

public E poll() {
        restartFromHead: for (;;) {
            for (Node<E> h = head, p = h, q;; p = q) {
                final E item;
            // 表头的数据不为null,并且“设置表头的数据为null”这个操作成功的话;
            // 则比较“p和h”(若p!=h,即表头发生了变化,则更新表头,即设置表头为p),
            // 然后返回原表头的item值。
                if ((item = p.item) != null && p.casItem(item, null)) {
                    // Successful CAS is the linearization point
                    // for item to be removed from this queue.
                    if (p != h) // hop two nodes at a time
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
                // 表头的下一个节点为null,即链表只有一个“内容为null的表头节点”。
                // 则更新表头为p,并返回null。
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
                else if (p == q)
                    continue restartFromHead;
            }
        }
    }

一个例子

import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * @author fitz.bai
 */
public class MyThread21 {

    private static Queue<String> queue = new ConcurrentLinkedQueue<>();

    public static void main(String[] args) {

        new MyThread("a").start();
        new MyThread("b").start();
    }

    private static void printAll() {
        String value;
        Iterator iter = queue.iterator();
        while (iter.hasNext()) {
            value = (String) iter.next();
            System.out.print(value + ", ");
        }
        System.out.println();
    }

    private static class MyThread extends Thread {
        MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            int i = 0;
            while (i++ < 6) {
                String val = Thread.currentThread().getName() + i;
                queue.add(val);
                printAll();
            }
        }
    }
}

//运行结果
b1, b1, a1, a1, 

b1, b1, a1, a1, a2, a2, b2, 
b2, 
b1, b1, a1, a1, a2, a2, b2, a3, b3, 
b1, a1, a2, b2, a3, b3, a4, 
b1, a1, a2, b2, a3, b3, a4, a5, 
b1, a1, a2, b2, a3, b3, a4, a5, a6, 
b2, a3, b3, a4, a5, a6, 
b1, a1, a2, b2, a3, b3, a4, a5, a6, b4, 
b1, a1, a2, b2, a3, b3, a4, a5, a6, b4, b5, 
b1, a1, a2, b2, a3, b3, a4, a5, a6, b4, b5, b6, 

ConcurrentLinkedQueue的规则还是比较复杂的,自己理解的也不是很好,先当一个笔记记着,后续再来修改,欢迎大家一起学习

猜你喜欢

转载自blog.csdn.net/qq_22798455/article/details/81637397