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的操作。看明白这几个方法,其他方法应该都没问题了。



    原文作者:JUC
    原文地址: https://blog.csdn.net/xiaoxufox/article/details/51887329
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞