Java Queue详解-从普通Queue到ConcurrentQueue

8 篇文章 0 订阅
3 篇文章 0 订阅

队列类关系总览

在这里插入图片描述

1. ArrayQueue

环形队列

public class ArrayQueue<T> extends AbstractList<T> {
    private int capacity; // 对象数组长度
    private T[] queue;	  // 对象数组
    private int head; 	  // 头指针
    private int tail;	  // 尾指针
}

常用方法

代码很简单,不多赘述,一看就能明白。不支持迭代器,遍历元素需要自己手动从head开始到tail

 public void resize(int newcapacity) {
     int size = size();
     if (newcapacity < size)
         throw new IndexOutOfBoundsException("Resizing would lose data");
     newcapacity++;
     if (newcapacity == this.capacity)
         return;
     T[] newqueue = newArray(newcapacity);
     for (int i = 0; i < size; i++)
         newqueue[i] = get(i);
     this.capacity = newcapacity;
     this.queue = newqueue;
     this.head = 0;
     this.tail = size;
 }


private T[] newArray(int size) {
    return (T[]) new Object[size];
}

public boolean add(T o) {
    queue[tail] = o;
    int newtail = (tail + 1) % capacity;
    if (newtail == head)
        throw new IndexOutOfBoundsException("Queue full");
    tail = newtail;
    return true; // we did add something
}

public T remove(int i) {
    if (i != 0)
        throw new IllegalArgumentException("Can only remove head of queue");
    if (head == tail)
        throw new IndexOutOfBoundsException("Queue empty");
    T removed = queue[head];
    queue[head] = null;
    head = (head + 1) % capacity;
    return removed;
}

public T get(int i) {
    int size = size();
    if (i < 0 || i >= size) {
        final String msg = "Index " + i + ", queue size " + size;
        throw new IndexOutOfBoundsException(msg);
    }
    int index = (head + i) % capacity;
    return queue[index];
}

public int size() {
    // Can't use % here because it's not mod: -3 % 2 is -1, not +1.
    int diff = tail - head;
    if (diff < 0)
        diff += capacity;
    return diff;
}

2. LinkedList

属性一览


transient int size = 0;

/**
 * Pointer to first node.
 */
transient Node<E> first;

/**
 * Pointer to last node.
 */
transient Node<E> last;


// 每个元素封装成一个内部类 节点类型,
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

常用方法

// 尾巴上添加元素
public boolean offer(E e) {
    return add(e);  // ----> 调用 linkLast(e);
}

public void add(int index, E element) {
     // ...
    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

// 返回第一个元素
 public E peek() {
     final Node<E> f = first;
     return (f == null) ? null : f.item;
 }

// 返回并删除第一个元素
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

// 在头上往里加,跟offer正好相反
public void push(E e) {
    addFirst(e);
}

迭代器

// 支持从指定位置开始进行迭代
ListItr(int index) {
    // assert isPositionIndex(index);
    next = (index == size) ? null : node(index);
    nextIndex = index;
}


public boolean hasNext() {
    return nextIndex < size;
}

public E next() {
    // .. 各种检查代码略
    lastReturned = next;  // lastReturned 就是当前要返回的节点
    next = next.next;
    nextIndex++;
    return lastReturned.item;
}

// 除了可以往下迭代之外,还可以往前迭代
// hasPrevious()
// previous()
// previousIndex()

3. ArrayDeque

双端循环队列,代码很简单不多讲。

public boolean add(E e) {
    addLast(e);
    return true;
}

public boolean offer(E e) {
    return offerLast(e);
}

public E poll() {
    return pollFirst();
}

public E pollLast() {
    final Object[] es;
    final int t;
    E e = elementAt(es = elements, t = dec(tail, es.length));
    if (e != null)
        es[tail = t] = null;
    return e;
}

public E peek() {
    return peekFirst();
}

public E peekLast() {
    final Object[] es;
    return elementAt(es = elements, dec(tail, es.length));
}

public void push(E e) {
    addFirst(e);
}

public E pop() {
    return removeFirst();
}

// 删除指定位置的元素
boolean delete(int i) {
    final Object[] es = elements;
    final int capacity = es.length;
    final int h, t;
    // 计算出这个删除的元素是属于前半截还是后半截
    final int front = sub(i, h = head, capacity);
    final int back = sub(t = tail, i, capacity) - 1;
    // 举例来说:
    // 如果删除第十个元素,现在总共有1000个元素,肯定把前9个往后挪代价更低
    if (front < back) {
        // move front elements forwards
        if (h <= i) {
            System.arraycopy(es, h, es, h + 1, front);
        } else { // Wrap around
            System.arraycopy(es, 0, es, 1, i);
            es[0] = es[capacity - 1];
            System.arraycopy(es, h, es, h + 1, front - (i + 1));
        }
        es[h] = null;
        head = inc(h, capacity);
        return false;
    } else {
        // move back elements backwards
        tail = dec(t, capacity);
        if (i <= tail) {
            System.arraycopy(es, i + 1, es, i, back);
        } else { // Wrap around
            System.arraycopy(es, i + 1, es, i, capacity - (i + 1));
            es[capacity - 1] = es[0];
            System.arraycopy(es, 1, es, 0, t - 1);
        }
        es[tail] = null;
        return true;
    }
}

private void grow(int needed) {
    // overflow-conscious code
    final int oldCapacity = elements.length;
    int newCapacity;
    // 如果当前容量 < 64 , 每次扩容直接翻倍容量, 否则只扩容50%
    int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1);
    if (jump < needed
        || (newCapacity = (oldCapacity + jump)) - MAX_ARRAY_SIZE > 0)
        newCapacity = newCapacity(needed, jump);
    // 把之前老数组中的值拷贝到新数组
    final Object[] es = elements = Arrays.copyOf(elements, newCapacity);
    // Exceptionally, here tail == head needs to be disambiguated
    if (tail < head || (tail == head && es[head] != null)) {
        // wrap around; slide first leg forward to end of array
        int newSpace = newCapacity - oldCapacity;
        System.arraycopy(es, head,
                         es, head + newSpace,
                         oldCapacity - head);
        for (int i = head, to = (head += newSpace); i < to; i++)
            es[i] = null;
    }
}

4. ArrayBlockingQueue

特性

  • 循环队列
  • ReentrantLock用作同步锁

常用方法

put()

当放不进去的时候,阻塞当前线程,直到放进去才继续执行代码

public void put(E e) throws InterruptedException {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    // 可以被打断的锁,加入等待队列之后可以取消自己的等待状态
    // 线程被打断需要更新自己在等待队列的前后关系
    lock.lockInterruptibly();
    try {
        // 如果当前队列已经满了,put线程进入 notFull 这个 condition队列阻塞
        while (count == items.length)
            // 这里会释放锁,然后unpark等待队列中的一个线程
            // 即使是condition.await, 也是没什么用的,也都是加到同一等待队列中
            // 源码逻辑既是如此
            // unpark的下一个线程有可能是消费者也有可能是生产者
            notFull.await();
        // 队列中放入元素之后,需要把其他消费者线程唤醒
        enqueue(e);
    } finally {
        lock.unlock();
    }
}
 
private void enqueue(E e) {
    // assert lock.isHeldByCurrentThread();
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = e;
    if (++putIndex == items.length) putIndex = 0;
    count++;
    // 队列中放入元素之后,需要把其他消费者线程唤醒
    // 虽然是这个这个condition,但是也都是去同一个队列中叫醒线程
    notEmpty.signal();
}

offer()

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

    Objects.requireNonNull(e);
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length) {
            if (nanos <= 0L)
                return false;
            // 过期就需要把自己取消掉,然后更新在等待队列中前后等待关系
            nanos = notFull.awaitNanos(nanos);
        }
        // 队列中放入元素之后,需要把其他消费者线程唤醒
        enqueue(e);
        return true;
    } finally {
        lock.unlock();
    }
}

private void enqueue(E e) {
    // assert lock.isHeldByCurrentThread();
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = e;
    if (++putIndex == items.length) putIndex = 0;
    count++;
    // 队列中放入元素之后,需要把其他消费者线程唤醒
    // signalAll使用nextWaiter指针叫醒全部等待对列中的线程
    notEmpty.signal();
}

poll()

队列为空

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

private E dequeue() {
    // assert lock.isHeldByCurrentThread();
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E e = (E) items[takeIndex];
    items[takeIndex] = null;
    // 还是循环队列啊
    if (++takeIndex == items.length) takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    // 叫醒生产者线程
    notFull.signal();
    return e;
}

take()

poll的阻塞版本

poll不出来等着

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

5. LinkedBlockingQueue

特性

  • 链表
  • ReentrantLock用作同步锁

理解了ArrayBlockingQueue + AQS之后,代码相差无几

6. LinkedBlockingDeque

特性

  • 双端队列,链表
  • ReentrantLock用作同步锁

理解了ArrayBlockingQueue + AQS之后,代码相差无几

7. PriorityQueue

特性

  • 优先级队列,使用小顶堆来排序

小根堆插入图解

priorityQueue.add(1);
priorityQueue.add(6);
priorityQueue.add(5);
priorityQueue.add(7);
priorityQueue.add(8);
priorityQueue.add(18);
priorityQueue.add(0);

while(!priorityQueue.isEmpty()){
    System.out.println(priorityQueue.poll());
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        // 数组不够长的时候进行数组扩容
        // 数组长度小于64,直接翻倍容量,
        // 过大的时候只扩容50%
        // 调用 System.arrayCopy 复制到新的数组中
        grow(i + 1);
    // 向上滑动找到合适的排放位置
    siftUp(i, e);
    size = i + 1;
    return true;
}

小根堆弹出图解

每次弹出堆顶元素

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


public E poll() {
    final Object[] es;
    final E result;

    if ((result = (E) ((es = queue)[0])) != null) {
        modCount++;
        final int n;
        final E x = (E) es[(n = --size)];
        es[n] = null;
        if (n > 0) {
            final Comparator<? super E> cmp;
            if ((cmp = comparator) == null)
                // 使用默认排序规则进行排序
                // 向下比较是否需要交换顺序
                siftDownComparable(0, x, es, n);
            else
                // 如果有比较器,就用自己定义好的比较器进行排序
                siftDownUsingComparator(0, x, es, n, cmp);
        }
    }
    return result;
}

8. DelayQueue

特性

  • 队列中只能put实现了Delayed接口的类型
  • 底层就是优先级队列PriorityQueue
  • 堆顶的元素要等着过期才可以取出,否则去取元素的线程就要park
  • 如果是第一个线程取就要park,要把自己设置为leader,根据第一个元素过期时间计算好park时间
  • leader醒过来取走然后还要叫醒其他等待队列的线程

加锁版的PriorityQueue

而且使用的了Condition等待队列

支持迭代器

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 小根堆排序
        q.offer(e);
        if (q.peek() == e) {
            leader = null;
            // condition 等待队列
            available.signal();
        }
        return true;
    } finally {
        lock.unlock();
    }
}


public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (;;) {
            // 看看队列中有没有元素
            E first = q.peek();
            //如果队列为空,则直接去等待队列等待
            if (first == null)
                available.await();
            else {
                // 看看第一个元素的过期时间
                long delay = first.getDelay(NANOSECONDS);
                if (delay <= 0L)
                    // 如果刚刚过期,或者已经过了,直接返回
                    return q.poll();
                // 否则就要等着过期才可以返回
                // 看看当前有没有线程等待
                // 如果有线程  leader 不为空
                // 自己则进入等待队列等待
                first = null; // don't retain ref while waiting
                if (leader != null)
                    available.await();
                else {
                    // 把自己设置成leader
                    // 自己等待过期的纳秒数,然后再来重新获取第一个元素
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        available.awaitNanos(delay);
                    } finally {
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        // 满足条件,叫醒所有等待队列中的线程
        if (leader == null && q.peek() != null)
            available.signal();
        lock.unlock();
    }
}

9. PriorityBlockingQueue

特性

  • 使用了 ReentrantLock用作同步锁,

  • 依然使用小顶堆排序,

  • 依然每次poll弹出堆顶元素

常用方法

take()

阻塞版本pollpoll不出去就await,依靠offer方法唤醒

poll()

除了加了个锁之外跟普通的PriorityQueue没什么区别

10. ConcurrentLinkedQueue

特性

  • 使用CAS+自旋,不再使用 ReentrantLock

内部类

// 存储的值被封装为Node
static final class Node<E> {
    volatile E item;
    volatile Node<E> next;
}

// 用VarHandle来指向所有的变量
// 可以直接获取变量的地址,进行CAS操作
private static final VarHandle HEAD;
private static final VarHandle TAIL;
static final VarHandle ITEM;
static final VarHandle NEXT;
static {
    try {
        MethodHandles.Lookup l = MethodHandles.lookup();
        HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head",
                               Node.class);
        TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail",
                               Node.class);
        ITEM = l.findVarHandle(Node.class, "item", Object.class);
        NEXT = l.findVarHandle(Node.class, "next", Node.class);
    } catch (ReflectiveOperationException e) {
        throw new ExceptionInInitializerError(e);
    }
}

常用方法

offer()

public boolean offer(E e) {
    final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));

    for (Node<E> t = tail, p = t;;) {
        // 有的时候tail指针竟然会指在倒数第二个位置
        // 此时 q 就不为空
        Node<E> q = p.next;
        if (q == null) {
            // p is last node
            // CAS操作替换 tail的next指针
            if (NEXT.compareAndSet(p, null, newNode)) {
                // Successful CAS is the linearization point
                // for e to become an element of this queue,
                // and for newNode to become "live".
                if (p != t) // hop two nodes at a time; failure is OK
                    // 继续用CAS操作设置新的尾巴为新创建的节点
                    TAIL.weakCompareAndSet(this, t, newNode);
                return true;
            }
            // Lost CAS race to another thread; re-read next
        }
        // 有的时候tail指针竟然会指在倒数第二个位置
        // 但大部分时候都是跳入下一个else中,
        // 什么逻辑会触发这个 else if 有待于继续试验
        else if (p == q)
            // We have fallen off list.  If tail is unchanged, it
            // will also be off-list, in which case we need to
            // jump to head, from which all live nodes are always
            // reachable.  Else the new tail is a better bet.
            p = (t != (t = tail)) ? t : head;
        else
            // 把tail指向当前q的位置
            // Check for tail updates after two hops.
            p = (p != t && t != (t = tail)) ? t : q;
    }
}

peek()

Node<E> first() {
    // continue回到这个地方 restartFromHead
    restartFromHead: for (;;) {
        for (Node<E> h = head, p = h, q;; p = q) {
            boolean hasItem = (p.item != null);
            if (hasItem || (q = p.next) == null) {
                // 有可能其他线程已经进行过了 poll,更改了头结点,
                updateHead(h, p);
                return hasItem ? p : null;
            }
            else if (p == q)
                // 看到了新的写法,倒时很有意思
                // continue回到 restartFromHead
                continue restartFromHead;
        }
    }
}

poll()

public E poll() {
    restartFromHead: for (;;) {
        for (Node<E> h = head, p = h, q;; p = q) {
            final E item;
            // CAS:使用 null替换当前头节点的值
            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);
                // 替换成功则返回 已经操作成功的Item的值
                return item;
            }
            else if ((q = p.next) == null) {
                updateHead(h, p);
                return null;
            }
            else if (p == q)
                continue restartFromHead;
        }
    }
}

11. ConcurrentLinkedDeque

特性

  • 双端队列,比ConcurrentLinkedQueue多了指向pre的指针,和从tail弹出、从head添加元素的操作,

  • 从获取tailhead节点,到改变节点的prenext指针全是CAS操作

  • 不再使用ReentrantLock用作同步锁

12. SynchronousQueue

特性

容量为0,相当于两个线程手把手递东西给对方。

其实准确讲队列容量不是为0,只是这个队列不是存放对象用的。每个线程去队列中 put / get 的时候,都是把自己注册进这个队列(也就是说这个队列是存放线程用)

  • 不管是取元素还是放元素都会入队;

  • 去队列中get的线程,模式为 REQUEST,去put的线程模式为DATA

  • 每个线程调用 put / get时,先看看队列头是什么请求,put 拿着 data 如果看到队列中第一个是 其他线程的get请求,则get线程直接退出阻塞,返回数据,如果队头的模式跟当前线程的模式一样,则自己注册进队列。公平模式的话,FIFO,自己进入队列尾(公平模式)或者栈顶(非公平模式)。非公平模式队列变成了一个栈,所以当前线程push进栈。

  • 反之亦然。

  • 入队之后,先自旋一定次数后再调用LockSupport.park()LockSupport.parkNanos阻塞;

  • 匹配之后解除阻塞,LockSupport.unpark(),保证线程解除阻塞之后能正常运转

  • SynchronousQueue没有使用synchronized、重入锁,基本是通过 自旋+CAS实现;

非公平模式图解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

也就是说SynchronousQueue的每一次put操作,必须等待其他线性的poll操作。

每一个poll操作也必须等待其他线程的put操作。

内部类

static final class SNode {
    volatile SNode next;        // next node in stack
    volatile SNode match;       // the node matched to this
    volatile Thread waiter;     // to control park/unpark
    Object item;                // data; or null for REQUESTs
    int mode;
}

take() + put()

public E take() throws InterruptedException {
    // 当一个线程去队列中take东西的时候,
    // 调用transfer方法,
    E e = transferer.transfer(null, false, 0);
    if (e != null)
        return e;
    Thread.interrupted();
    throw new InterruptedException();
}

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    // 当一个线程去队列中put东西的时候,
    // 调用transfer方法,
    if (transferer.transfer(e, false, 0) == null) {
        Thread.interrupted();
        throw new InterruptedException();
    }
}


// 调用 put get 真正干活的方法
E transfer(E e, boolean timed, long nanos) {
    
    SNode s = null; // constructed/reused as needed
    // 如果取东西,则模式为 request 模式
    int mode = (e == null) ? REQUEST : DATA;

    for (;;) {
        // 公平模式下 交换队列是一个真正的队列,FIFO,head是队头
        // 非公平模式模式下,交换队列是一个栈,head是栈顶
        SNode h = head;
        // 如果交换队列为空,或者两个模式相等
        if (h == null || h.mode == mode) {  // empty or same-mode
            if (timed && nanos <= 0L) {     // can't wait
                if (h != null && h.isCancelled())
                    casHead(h, h.next);     // pop cancelled node
                else
                    return null;
                // snode方法为新请求或者数据创建节点,然后CAS替换为自己头结点
                //(LIFO非公平队列,公平队列的逻辑也差不多)
            } else if (casHead(h, s = snode(s, e, h, mode))) {
                // 尝试获取匹配线程,如果长时间匹配不到 park线程
                // 如果匹配线程竟然是自己,表示这个节点已经被取消了(里边的线程已经超时或者被打断),清除当前节点,
                SNode m = awaitFulfill(s, timed, nanos);
                if (m == s) {               // wait was cancelled
                    clean(s);
                    return null;
                }
                // 取出之后更新头结点
                if ((h = head) != null && h.next == s)
                    casHead(h, s.next);     // help s's fulfiller
                // 更新完成之后返回数据
                return (E) ((mode == REQUEST) ? m.item : s.item);
            }
        } 
        // 如果两个模式不一样,就是说一个take 碰见了 一个 put
        // put 的 那个线程 可以给 take 的这个线程数据了
        else if (!isFulfilling(h.mode)) { // try to fulfill
            if (h.isCancelled())            // already cancelled
                casHead(h, h.next);         // pop and retry
            else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
                for (;;) { // loop until matched or waiters disappear
                    SNode m = s.next;       // m is s's match
                    if (m == null) {        // all waiters are gone
                        casHead(s, null);   // pop fulfill node
                        s = null;           // use new node next time
                        break;              // restart main loop
                    }
                    SNode mn = m.next;
                    // 线程已经找到CP,弹出两个节点
                    // 并在 tryMatch 中调用 LockSupport.unpark,保证弹出线程可以正常运行
                    if (m.tryMatch(s)) {
                        casHead(s, mn);     // pop both s and m
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    } else                  // lost match
                        s.casNext(m, mn);   // help unlink
                }
            }
        } else {                            // help a fulfiller
            SNode m = h.next;               // m is h's match
            if (m == null)                  // waiter is gone
                casHead(h, null);           // pop fulfilling node
            else {
                SNode mn = m.next;
                if (m.tryMatch(h))          // help match
                    casHead(h, mn);         // pop both h and m
                else                        // lost match
                    h.casNext(m, mn);       // help unlink
            }
        }
    }
}



 SNode awaitFulfill(SNode s, boolean timed, long nanos) {

     final long deadline = timed ? System.nanoTime() + nanos : 0L;
     Thread w = Thread.currentThread();
     int spins = shouldSpin(s)
         ? (timed ? MAX_TIMED_SPINS : MAX_UNTIMED_SPINS)
         : 0;
     for (;;) {
         // 如果线程被打断,用CAS操作替换掉s中的waiter线程为null
         if (w.isInterrupted())
             s.tryCancel();
         // 看看当前node有没有匹配的线程
         // 如果有了,则直接返回匹配的那个节点
         SNode m = s.match;
         if (m != null)
             return m;
         // 如果设置了过期时间,看看线程有没有过期,过期直接取消线程
         if (timed) {
             nanos = deadline - System.nanoTime();
             if (nanos <= 0L) {
                 s.tryCancel();
                 continue;
             }
         }
         // 如果暂时还不用park,还有剩余的自旋次数(最多64次)
         if (spins > 0) {
             Thread.onSpinWait();
             spins = shouldSpin(s) ? (spins - 1) : 0;
         }
         // 如果自己这个节点的waiter为空,把自己设置进这个节点的waiter中,
         // 等待匹配之后能知道叫醒哪个线程
         else if (s.waiter == null)
             s.waiter = w; // establish waiter so can park next iter
         // 阻塞线程,停止自旋
         else if (!timed)
             LockSupport.park(this);
         else if (nanos > SPIN_FOR_TIMEOUT_THRESHOLD)
             LockSupport.parkNanos(this, nanos);
     }
 }

13. LinkedTransferQueue

特性

  • LinkedTransferQueue可以看作SynchronousQueue(公平模式)、ConcurrentLinkedQueue二者的合体;

  • 不管是取元素还是放元素都会入队;

  • 至于是否入队及阻塞有四种模式,NOW,ASYNC,SYNC,TIMED

  • 入队之后,先自旋一定次数后再调用LockSupport.park()LockSupport.parkNanos阻塞;

  • 匹配之后解除阻塞,LockSupport.unpark(),保证线程解除阻塞之后能正常运转

  • SynchronousQueue没有使用synchronized、重入锁,基本是通过 自旋+CAS实现;

  • put线程不用阻塞直接返回继续执行,相当于异步交付任务

  • take线程是同步方法,取不到会阻塞

常用方法

put(), ASYNC

public void put(E e) {
    // 调用xfer方法,参数为 ASYNC
    xfer(e, true, ASYNC, 0);
}

private E xfer(E e, boolean haveData, int how, long nanos) {
    if (haveData && (e == null))
        throw new NullPointerException();

    restart: for (Node s = null, t = null, h = null;;) {
        // put 模式,先把 p 直线队尾
        for (Node p = (t != (t = tail) && t.isData == haveData) ? t
             : (h = head);; ) {
            final Node q; final Object item;
            // 如果当前队尾的是调用take方法的线程插进来的
            if (p.isData != haveData
                && haveData == ((item = p.item) == null)) {
                if (h == null) h = head;
                // 直接匹配队尾的元素给当前线程,
                // 然后调用 LockSupport.unpark(),解除线程的阻塞
                if (p.tryMatch(item, e)) {
                    if (h != p) skipDeadNodesNearHead(h, p);
                    return (E) item;
                }
            }
            // 如果队尾的节点模式是一样的,就要用CAS操作把这个put元素加到队尾
            if ((q = p.next) == null) {
                if (how == NOW) return e;
                if (s == null) s = new Node(e);
                if (!p.casNext(null, s)) continue;
                if (p != t) casTail(t, s);
                // put模式都是 ASYNC,所以put线程不用阻塞直接返回继续执行。相当于异步交付任务
                if (how == ASYNC) return e;
                return awaitMatch(s, p, e, (how == TIMED), nanos);
            }
            if (p == (p = q)) continue restart;
        }
    }
}

take(), SYNC

  • 跟put代码一样,只是这次入队的是 take的线程,而且take不到就要阻塞

private E xfer(E e, boolean haveData, int how, long nanos) {
    if (haveData && (e == null))
        throw new NullPointerException();

    restart: for (Node s = null, t = null, h = null;;) {
        // take 模式,先把 p 直线队尾
        for (Node p = (t != (t = tail) && t.isData == haveData) ? t
             : (h = head);; ) {
            final Node q; final Object item;
            // 如果当前队尾的是调用take方法的线程插进来的
            if (p.isData != haveData
                && haveData == ((item = p.item) == null)) {
                if (h == null) h = head;
                // 直接匹配队尾的元素给当前线程,
                // 然后调用 LockSupport.unpark(),解除线程的阻塞
                if (p.tryMatch(item, e)) {
                    if (h != p) skipDeadNodesNearHead(h, p);
                    return (E) item;
                }
            }
            // 如果队尾的节点模式是一样的,就要用CAS操作把这个put元素加到队尾
            if ((q = p.next) == null) {
                if (how == NOW) return e;
                if (s == null) s = new Node(e);
                if (!p.casNext(null, s)) continue;
                if (p != t) casTail(t, s);
                if (how == ASYNC) return e;
                // 既然这次是take操作,操作码为 SYNC, 跳入此方法执行,awaitMatch 等待匹配
                // 先自旋一段时间,然后调用LockSupport.park
                return awaitMatch(s, p, e, (how == TIMED), nanos);
            }
            if (p == (p = q)) continue restart;
        }
    }
}

remove()

  • 移除元素的时候,把等待队列中的元素删除的同时要保证线程不被继续阻塞(因为有可能此时等待队列中的为take线程),所以仍然需要调用 tryMatch方法中的LockSupport.unpark
  • 此时的逻辑是传参数为nulltryMatch(item, null)
  • 之前的逻辑是用新创建的匹配节点,如果匹配成功就把两个出队
public boolean remove(Object o) {
    if (o == null) return false;
    restartFromHead: for (;;) {
        for (Node p = head, pred = null; p != null; ) {
            Node q = p.next;
            final Object item;
            if ((item = p.item) != null) {
                if (p.isData) {
                    // 移除元素的时候,把等待队列中的元素删除的同时要保证线程不被继续阻塞
                    // (因为有可能此时等待队列中的为`take`线程),
                    // 所以仍然需要调用 `tryMatch`方法中的`LockSupport.unpark`
                    if (o.equals(item) && p.tryMatch(item, null)) {
                        skipDeadNodes(pred, p, p, q);
                        return true;
                    }
                    pred = p; p = q; continue;
                }
            }
            else if (!p.isData)
                break;
            for (Node c = p;; q = p.next) {
                if (q == null || !q.isMatched()) {
                    pred = skipDeadNodes(pred, c, p, q); p = q; break;
                }
                if (p == (p = q)) continue restartFromHead;
            }
        }
        return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值