Java同步队列(非阻塞队列与阻塞队列)——java并发容器

在并发编程中,有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法。

使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现。

非阻塞的实现方式则可以使用循环CAS的方式来实现。

ConcurrentLinkedQueue

我们一起来研究一下如何使用非阻的方式来实现线程安全队列ConcurrentLinkedQueue的
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部;当我们获取一个元素时,它会返回队列头部的元素。

ConcurrentLinkedQueue结构

public class ConcurrentLinkedQueue extends AbstractQueue
        implements Queue, java.io.Serializable {
    private transient volatile Node head;//头指针
    private transient volatile Node tail;//尾指针
    public ConcurrentLinkedQueue() {//初始化,head=tail=(一个空的头结点)
        head = tail = new Node(null);
    }
    private static class Node {
        volatile E item;
        volatile Node next;//内部是使用单向链表实现
        ......
    }
    ......
}
Java同步队列(非阻塞队列与阻塞队列)——java并发容器_第1张图片

入队

    public boolean offer(E e) {
        checkNotNull(e);
        final Node newNode = new Node(e);//入队前,创建一个新节点

        for (Node t = tail, p = t;;) {//除非插入成功并返回,否则反复循环
            Node q = p.next;
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {//利用CAS操作,将p的next指针从旧值null更新为newNode 
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.利用CAS操作更新tail,如果失败说明其他线程添加了元素,由其他线程负责更新tail
                    return true;
                }
                // Lost CAS race to another thread; re-read next 如果添加元素失败,说明其他线程添加了元素,p后移,并继续尝试
            }
            else if (p == q) //如果p被移除出链表,我们需要调整指针重新指向head,否则我们指向新的tail
                p = (t != (t = tail)) ? t : head;
            else
                //p指向tail或者q
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

casTail(cmp,value)方法用于更新tail节点。tail被设置为volatile保证可见性。

p.casNext(cmp,value)方法用于将入队节点设置为当前队列尾节点的next节点。value也被设置为volatile。

对于出队操作,也是使用CAS的方式循环尝试将元素从头部移除。

因为采用CAS操作,允许多个线程并发执行,并且不会因为加锁而阻塞线程,使得并发性能更好。

Java中的阻塞队列

本节将介绍什么是阻塞队列,以及Java中阻塞队列的4种处理方式,并介绍Java 7中提供的7种阻塞队列,最后分析阻塞队列的一种实现方式。

什么是阻塞队列

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。
1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
2)支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。
阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

JDK 7提供了7个阻塞队列,如下。
·ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
·LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
·PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
·DelayQueue:一个使用优先级队列实现的无界阻塞队列。
·SynchronousQueue:一个不存储元素的阻塞队列。
·LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
·LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

内容来自:

《并发编程的艺术》

你可能感兴趣的:(Java并发编程)