JUC源码分析21-队列-LinkedBlockingDeque

LinkedBlockingDeque基于双向链表实现的阻塞队列,根据构造传入的容量大小决定有界还是无界,默认不传的话,大小Integer.Max。

实现BlockingDequeue接口,这个接口继承BlockingQueue和Dequeue,看下接口方法:

public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
	/** Deque 方法 */
    /**
     * 插入元素到队列头,队列满就IllegalStateException异常
     */
    void addFirst(E e);

    /**
     * 插入元素到队列尾,队列满就IllegalStateException异常
     */
    void addLast(E e);

    /**
     * 插入元素到队列头,队列满就false
     */
    boolean offerFirst(E e);

    /**
     * 插入元素到队列尾,队列满就false
     */
    boolean offerLast(E e);

    /**
     * 上面的几个插入方法,如果插入时队列满就返回异常或false,这里put操作加入条件队列wait
     */
    void putFirst(E e) throws InterruptedException;

    /**
     * 插入元素到队列尾,队列满就加入条件队列wait
     */
    void putLast(E e) throws InterruptedException;

    /**
     * 响应超时的offer插入队列头
     */
    boolean offerFirst(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 响应超时的offer插入队列尾
     */
    boolean offerLast(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 获取队列头,队列不可用就wait
     */
    E takeFirst() throws InterruptedException;

    /**
     * 获取队列尾,队列不可用就wait
     */
    E takeLast() throws InterruptedException;

    /**
     * 响应超时的获取队列头
     */
    E pollFirst(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 响应超时的获取队列尾
     */
    E pollLast(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 删除第一个匹配的指定元素
     */
    boolean removeFirstOccurrence(Object o);

    /**
     * 删除最后一个匹配的指定元素
     */
    boolean removeLastOccurrence(Object o);

    // *** BlockingQueue methods 之前看过***

    boolean add(E e);

    boolean offer(E e);

    void put(E e) throws InterruptedException;

    boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

    E remove();

    E poll();

    E take() throws InterruptedException;

    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 获取队列头,跟peek不同的是,队列空,抛异常
     */
    E element();

    E peek();

    boolean remove(Object o);

    public boolean contains(Object o);

    public int size();

    Iterator<E> iterator();

    // *** Stack methods ***

    /**
     * addFirst
     */
    void push(E e);
}

因为是双向结构,所以基本的操作成对的XXXFirst,XXXLast。

/** 双向链表节点 */
static final class Node<E> {
    /**
     * 元素值
     */
    E item;

    /**
     * 节点前驱
	 * 1.指向前驱;2.指向this,说明前驱是尾节点,看unlinklast;3.指向null说明没有前驱
     */
    Node<E> prev;

    /**
     * 节点后继
	 * 1.指向后继;2.指向this,说明后继是头结点,看unlinkfirst;3.指向null说明没有后继
     */
    Node<E> next;

    Node(E x) {
        item = x;
    }
}

/**
 * 首节点
 */
transient Node<E> first;

/**
 * 尾节点
 */
transient Node<E> last;

/** 队列元素个数 */
private transient int count;

/** 队列容量 */
private final int capacity;

/** Main lock guarding all access */
final ReentrantLock lock = new ReentrantLock();

/** 获取的条件等待:非空条件 */
private final Condition notEmpty = lock.newCondition();

/** 插入元素的条件等待:非满条件 */
private final Condition notFull = lock.newCondition();

/**
 * 空构造,默认容量最大
 */
public LinkedBlockingDeque() {
    this(Integer.MAX_VALUE);
}

/**
 * 指定容量
 */
public LinkedBlockingDeque(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
}

构造的时候可以传入链表容量大小,没传入就Integer.MAX。

看下最基础的几个方法:

// 4个基础方法,Dequeue的操作基本都是调用这些
/**
 * 设置node为链表头节点,链表满时为false
 */
private boolean linkFirst(Node<E> node) {
    // assert lock.isHeldByCurrentThread();
    if (count >= capacity) //超过容量false
        return false;
    Node<E> f = first;
    node.next = f; //新节点的next指向原first
    first = node; //设置node为新的first
    if (last == null) //没有尾节点,就将node设置成尾节点
        last = node;
    else
        f.prev = node; //有尾节点,那就将之前first的pre指向新增node
    ++count; //累加节点数量
    notEmpty.signal(); //有新节点入队,通知非空条件队列
    return true;
}

/**
 * 设置node为链表尾节点,链表满时为false
 */
private boolean linkLast(Node<E> node) {
    // assert lock.isHeldByCurrentThread();
    if (count >= capacity)
        return false;
    Node<E> l = last;
    node.prev = l;
    last = node;
    if (first == null) //为null,说明之前队列空吧,那就first也指向node
        first = node;
    else
        l.next = node; //非null,说明之前的last有值,就将之前的last的next指向node
    ++count;
    notEmpty.signal();
    return true;
}

/**
 * 移除头结点,链表空返回null
 */
private E unlinkFirst() {
    // assert lock.isHeldByCurrentThread();
    Node<E> f = first;
    if (f == null)
        return null; //空返回null
    Node<E> n = f.next;
    E item = f.item;
    f.item = null;
    f.next = f; // help GC
    first = n;
    if (n == null) //说明之前应该只有一个节点,移除头结点后,链表空,现在first和last都指向null了
        last = null;
    else
        n.prev = null; //否则的话,n的pre原来指向之前的first,现在n变为first了,pre指向null
    --count;
    notFull.signal(); //通知非满条件队列
    return item;
}

/**
 * 移除尾结点,链表空返回null
 */
private E unlinkLast() {
    // assert lock.isHeldByCurrentThread();
    Node<E> l = last;
    if (l == null)
        return null;
    Node<E> p = l.prev;
    E item = l.item;
    l.item = null;
    l.prev = l; // help GC
    last = p;
    if (p == null)
        first = null;
    else
        p.next = null;
    --count;
    notFull.signal();
    return item;
}

/**
 * 移除指定节点:p--》x--》n
 */
void unlink(Node<E> x) {
    // assert lock.isHeldByCurrentThread();
    Node<E> p = x.prev;
    Node<E> n = x.next;
    if (p == null) { //prev为null说明x节点为头结点
        unlinkFirst();
    } else if (n == null) {
        unlinkLast(); //nex为null说明待清除节点为尾节点
    } else { //否则的话节点处于链表中间
        p.next = n; //将p和n互链
        n.prev = p;
        x.item = null;
        // 没有断开x节点链接,可能有其他线程在迭代链表
        --count;
        notFull.signal();
    }
}

大部分的Deque的实现都是调用这几个方法。因为是双端队列,所以link\unlink都有针对first和last的操作。看明白这几个方法,其他方法应该都没问题了。

时间: 2024-10-13 21:04:31

JUC源码分析21-队列-LinkedBlockingDeque的相关文章

JUC源码分析-集合篇(五)BlockingQueue 阻塞式队列实现原理

JUC源码分析-集合篇(五)BlockingQueue 阻塞式队列实现原理 以 LinkedBlockingQueue 分析 BlockingQueue 阻塞式队列的实现原理. 1. 数据结构 LinkedBlockingQueue 和 ConcurrentLinkedQueue 一样都是由 head 节点和 last 节点组成,每个节点(Node)由节点元素(item)和指向下一个节点(next)的引用组成,节点与节点之间就是通过这个 next 关联起来,从而组成一张链表结构的队列.默认情况下

JUC源码分析-集合篇(三)ConcurrentLinkedQueue

JUC源码分析-集合篇(三)ConcurrentLinkedQueue 在并发编程中,有时候需要使用线程安全的队列.如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法.使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现.非阻塞的实现方 式则可以使用循环 CAS 的方式来实现.本节让我们一起来研究一下 Doug Lea 是如何使用非阻塞的方式来实现线程安全队列 ConcurrentLinkedQueue 的,相信从大师

JUC源码分析-集合篇(七)PriorityBlockingQueue

JUC源码分析-集合篇(七)PriorityBlockingQueue PriorityBlockingQueue 是带优先级的无界阻塞队列,每次出队都返回优先级最高的元素,是二叉树最小堆的实现. PriorityBlockingQueue 数据结构和 PriorityQueue 一致,而线程安全性使用的是 ReentrantLock. 1. 基本属性 // 最大可分配队列容量 Integer.MAX_VALUE - 8,减 8 是因为有的 VM 实现在数组头有些内容 private stati

Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二)

Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二) 题记:  前文<Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)>中提到Recovery有两种策略,一是PeerSync和Replication.本节将具体介绍下PeerSync策略. PeeySync是Solr的优先选择策略,每当需要进行recovery了,Solr总是会先去判断是否需要进入PeerSync,只有当PeerSync被设置为跳过或者PeerSync时候发现没符合

JUC源码分析16-集合-ConcurrentSkipListMap、ConcurrentSkipListSet

NBA这赛季结束,勇士可惜啊,谁能想到没拿到冠军,库昊也没成为真正的老大,lbl一战封神,所有口水留言都变成羡慕嫉妒恨,哎,我库啊,还是还是看书吧. ConcurrentSkipListMap说实话,之前还真没注意过,还是看JUC才看到,利用skiplist跳表结构来实现一种有序的map,之前看到的map都是无序.在学习前还是要好好了解下什么是skiplist跳表,的确很不错,利用空间换时间,复杂度为logN,跳表的原理参考http://kenby.iteye.com/blog/1187303,

jQuery 源码分析(十一) 队列模块 Queue详解

队列是常用的数据结构之一,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).特点是先进先出,最先插入的元素最先被删除. 在jQuery内部,队列模块为动画模块提供基础功能,负责存储动画函数.自动出队并执行动画函数,同时还要确保动画函数的顺序执行. jQuery的静态方法含有如下API: $.queue(elem,type,data) ;返回或修改匹配元素关联的队列,返回最新的队列,参数如下:   elem ;DOM元素或JavaScript对象 type  ;

谷歌浏览器的源码分析 21

分享一下我老师大神的人工智能教程吧.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net 上一次说到类RenderThread和类RenderView把消息处理,那么这两个类是怎么样处理消息的呢?又是怎么样处理浏览的消息呢?现在就带着这两个问题去分析它的源码,理解它处理消息的方法.类RenderThread处理消息的代码如下:<?xml:namespace prefix = o ns = "urn:schemas-

memcached源码分析-----LRU队列与item结构体

转载请注明出处:http://blog.csdn.net/luotuo44/article/details/42869325 LRU队列: 之前的<slab内存分配>博文已经说到一个slab class里面的所有slab分配器都只分配相同大小的item,不同的slab class分配不同大小的item.item结构体里面有一个slabs_clsid成员,用来指明自己是属于哪个slab class的.这里把slabs_clsid值相同的item称为是同一类item. slab分配器负责分配一个i

JUC源码分析6-locks-AQS-独占模式

AbstractQueuedSynchronizer(下面简称AQS),javadoc说明: Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on  first-in-first-out (FIFO) wait queues. 1.提供一个FIFO等待队列,使用方法伪代码表示就是: Acquire: if(!获取到锁