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