【Java Collection】Queue 剖析(四)

我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码


一、架构

1.1 常见子类 UML

在这里插入图片描述
=== 点击查看top目录 ===

二、 Queue 接口

package java.util;
public interface Queue<E> extends Collection<E> {
    boolean add(E e);
    boolean offer(E e);
    E remove();
    E poll();
    E element();
    E peek();
}

=== 点击查看top目录 ===

三、 AbstractQueue 抽象类

3.1 代码架构
  • java.util 包
package java.util;

public abstract class AbstractQueue<E>
    extends AbstractCollection<E>
    implements Queue<E> {
    protected AbstractQueue() {}
    public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    public void clear() {
        while (poll() != null)
            ;
    }

    public boolean addAll(Collection<? extends E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
}
3.2 方法列表
方法备注
add添加元素,内部是offer 推进去
addAll添加一整个Collection的元素
remove删除元素,内部是 poll 拉出来
element拿出一个Element ,不remove
clear清除元素,内部死循环poll
3.3 通用方法区别

若queue是空的:remove 抛出异常,take 循环直到拿到元素,poll 返回null
若queue是空的:peek返回null
若queue是满的:add 抛出异常,offer返回 false,put循环直到能塞入元素

=== 点击查看top目录 ===

四、BlockingQueue 接口

在这里插入图片描述

4.1 代码架构
  • java.util.concurrent 包
package java.util.concurrent;
import java.util.Collection;
import java.util.Queue;

public interface BlockingQueue<E> extends Queue<E> {
    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 take() throws InterruptedException;
    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;
    int remainingCapacity();
    boolean remove(Object o);
    public boolean contains(Object o);
    int drainTo(Collection<? super E> c);
    int drainTo(Collection<? super E> c, int maxElements);
}
4.2 方法列表
方法备注
add-
offer-
put队列满了,阻塞等一会塞进去
offer队列满了,阻塞等一段时间塞进去,时间过了queue还没位置塞入就退出了
take队列空了,阻塞等待领取
poll队列空了,阻塞等一段时间,时间过了queue还没元素拿出就退出了
remainingCapacity查看 queue 剩余多少容量
remove移除元素
contains是否包含某个元素
drainTo一个 Collection 倒到另一个

=== 点击查看top目录 ===


五 、子类 LinkedBlockingQueue

  • 从 tail 插进去,从 head 取出来

  • 内部是链表结构

  • LinkedBlockingQueue 既继承了 AbstractQueue抽象类又实现了 BlockingQueue

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

=== 点击查看top目录 ===

5.1 变量
变量备注
private final int capacityqueue 容量,默认Integer.MAX_VALUE,注意是 final类型
private final AtomicInteger countqueue内数量,CAS
transient Node head;queue头 ,注意 transient
private transient Node last;queue的尾巴,注意 transient
private final ReentrantLock takeLock取锁take锁
private final Condition notEmpty = takeLock.newCondition();queue非空Condition
private final ReentrantLock putLock塞锁put锁
private final Condition notFull = putLock.newCondition();queue非满Condition

=== 点击查看top目录 ===

5.2 方法
  • 从 tail 插进去,从 head 取出来
方法备注
public int size()CAS获取queue数量
public int remainingCapacity()queue 剩余容量
public void put(E e) throws InterruptedExceptionblocking 等待 put 进去元素
public boolean offer(E e, long timeout, TimeUnit unit)blocking put 进去元素 尝试一段时间
public boolean offer(E e)blocking到死,一定要put进去
public E take() throws InterruptedExceptionblocking 等待获取元素
public E poll(long timeout, TimeUnit unit)blocking 等待获取元素(尝试一段时间)
public E poll()blocking到死,一定要 poll 出来
public E peek()拿到queue的队头 first 的Element
public boolean remove(Object o)先 fullyLock ,然后删掉某个元素
public boolean contains(Object o)先 fullyLock ,在查看元素是否存在
public Object[] toArray()先 fullyLock ,再转成数组Array
public T[] toArray(T[] a)先 fullyLock ,再转成数组Array 放到 T[] a 里面去
public void clear()先 fullyLock,再把内部元素给清空了
public int drainTo(Collection<? super E> c, int maxElements)先 takeLock,从this中倒出来maxElements 个元素到c里面去。
private void writeObject(java.io.ObjectOutputStream s)把对象写到输出流,内部重写
private void readObject(java.io.ObjectInputStream s)把输入流变成对象,内部重写
public Iterator iterator()得到内部类 Itr
public Spliterator spliterator()得到内部类 new LBQSpliterator(this)
  • 每次 put 都会加上 putLock,满了就会notFull.wait() ,有空间了就 notFull.signal()
  • 每次 take 都会加上 takeLock,空了就会 notEmpty.wait(),有货了就 notEmpty.signal()

=== 点击查看top目录 ===

5.3 static 内部类 Node
static class Node<E> {
    E item;
    Node<E> next;
    Node(E x) { item = x; }
}

=== 点击查看top目录 ===

5.4 private 内部类 Itr
private class Itr implements Iterator<E> {

        private Node<E> current;
        private Node<E> lastRet;
        private E currentElement;

        Itr() {
            fullyLock();
            try {
                current = head.next;
                if (current != null)
                    currentElement = current.item;
            } finally {
                fullyUnlock();
            }
        }

        public boolean hasNext() {
            return current != null;
        }

        private Node<E> nextNode(Node<E> p) {
            for (;;) {
                Node<E> s = p.next;
                if (s == p)
                    return head.next;
                if (s == null || s.item != null)
                    return s;
                p = s;
            }
        }

        public E next() {
            fullyLock();
            try {
                if (current == null)
                    throw new NoSuchElementException();
                E x = currentElement;
                lastRet = current;
                current = nextNode(current);
                currentElement = (current == null) ? null : current.item;
                return x;
            } finally {
                fullyUnlock();
            }
        }

        public void remove() {
            if (lastRet == null)
                throw new IllegalStateException();
            fullyLock();
            try {
                Node<E> node = lastRet;
                lastRet = null;
                for (Node<E> trail = head, p = trail.next;
                     p != null;
                     trail = p, p = p.next) {
                    if (p == node) {
                        unlink(p, trail);
                        break;
                    }
                }
            } finally {
                fullyUnlock();
            }
        }
    }
  • 注意: next() 方法与remove()方法会加fullyLock。

=== 点击查看top目录 ===

5.5 静态内部类LBQSpliterator
static final class LBQSpliterator<E> implements Spliterator<E> {
        static final int MAX_BATCH = 1 << 25;  // max batch array size;
        final LinkedBlockingQueue<E> queue;
        Node<E> current;    // current node; null until initialized
        int batch;          // batch size for splits
        boolean exhausted;  // true when no more nodes
        long est;           // size estimate
        LBQSpliterator(LinkedBlockingQueue<E> queue) {
            this.queue = queue;
            this.est = queue.size();
        }

        public long estimateSize() { return est; }

        public Spliterator<E> trySplit() {
            Node<E> h;
            final LinkedBlockingQueue<E> q = this.queue;
            int b = batch;
            int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
            if (!exhausted &&
                ((h = current) != null || (h = q.head.next) != null) &&
                h.next != null) {
                Object[] a = new Object[n];
                int i = 0;
                Node<E> p = current;
                q.fullyLock();
                try {
                    if (p != null || (p = q.head.next) != null) {
                        do {
                            if ((a[i] = p.item) != null)
                                ++i;
                        } while ((p = p.next) != null && i < n);
                    }
                } finally {
                    q.fullyUnlock();
                }
                if ((current = p) == null) {
                    est = 0L;
                    exhausted = true;
                }
                else if ((est -= i) < 0L)
                    est = 0L;
                if (i > 0) {
                    batch = i;
                    return Spliterators.spliterator
                        (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
                         Spliterator.CONCURRENT);
                }
            }
            return null;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            if (action == null) throw new NullPointerException();
            final LinkedBlockingQueue<E> q = this.queue;
            if (!exhausted) {
                exhausted = true;
                Node<E> p = current;
                do {
                    E e = null;
                    q.fullyLock();
                    try {
                        if (p == null)
                            p = q.head.next;
                        while (p != null) {
                            e = p.item;
                            p = p.next;
                            if (e != null)
                                break;
                        }
                    } finally {
                        q.fullyUnlock();
                    }
                    if (e != null)
                        action.accept(e);
                } while (p != null);
            }
        }

        public boolean tryAdvance(Consumer<? super E> action) {
            if (action == null) throw new NullPointerException();
            final LinkedBlockingQueue<E> q = this.queue;
            if (!exhausted) {
                E e = null;
                q.fullyLock();
                try {
                    if (current == null)
                        current = q.head.next;
                    while (current != null) {
                        e = current.item;
                        current = current.next;
                        if (e != null)
                            break;
                    }
                } finally {
                    q.fullyUnlock();
                }
                if (current == null)
                    exhausted = true;
                if (e != null) {
                    action.accept(e);
                    return true;
                }
            }
            return false;
        }

        public int characteristics() {
            return Spliterator.ORDERED | Spliterator.NONNULL |
                Spliterator.CONCURRENT;
        }
    }

=== 点击查看top目录 ===

5.6 实战 Executors
5.6.1 Executors#newFixedThreadPool
  • java.util.concurrent.Executors#newFixedThreadPool(int)
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • java.util.concurrent.Executors#newFixedThreadPool(int, java.util.concurrent.ThreadFactory)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
5.6.2 Executors#newSingleThreadExecutor
  • java.util.concurrent.Executors#newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  • java.util.concurrent.Executors#newSingleThreadExecutor(java.util.concurrent.ThreadFactory)
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

=== 点击查看top目录 ===

六、子类 ArrayBlockingQueue

  • 从 tail 插进去,从 head 取出来
  • 内部是数组结构
  • LinkedBlockingQueue 既继承了 AbstractQueue抽象类又实现了 BlockingQueue,跟 LinkedBlockingQueue 一样
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
6.1 变量
变量备注
final Object[] items;存储数据的数组
int takeIndex;take 的下标
int putIndex;put 的下标
int count;Queue中的元素数量
final ReentrantLock lock;锁,存+放共用一个锁
private final Condition notEmpty;未空Condition,表示可以取 take
private final Condition notFull;未满Condition,表示可以塞 put
transient Itrs itrs = null;遍历用的迭代器

=== 点击查看top目录 ===

6.2 方法
方法备注
public boolean add(E e)内部调用 offer
public boolean offer(E e)先 lock.lock() ,插尾巴,满了就失败,失败直接返回
public boolean offer(E e, long timeout, TimeUnit unit)先 lock.lock() ,插尾巴,满了就失败,失败循环尝试 N毫秒,依旧不行就返回失败
public void put(E e) throws InterruptedException先 lock.lock() ,插尾巴,满了就失败,失败就直接无限循环(可以打断)
public E peek()先 lock.lock() ,从头部拉一个元素出来,没元素返回 nul
public E poll()先 lock.lock() ,从头部拉一个元素出来,没元素返回 null
public E poll(long timeout, TimeUnit unit) throws InterruptedException先 lock.lock() ,从头部拉一个元素出来,没元素尝试一段时间,最后还是不行就返回 null
public E take() throws InterruptedException先 lock.lock() ,空了就失败,,失败就直接无限循环(可以打断)
public int size()先 lock.lock() ,再取size
public int remainingCapacity()先 lock.lock() ,再查剩余容量
public boolean remove(Object o)先 lock.lock() ,然后从头删掉一个
public boolean contains(Object o)先 lock.lock() ,再去看内部是否contains
public Object[] toArray()先 lock.lock() ,再底层调用 System.arraycopy 进行数组的复制
public T[] toArray(T[] a)先 lock.lock() ,复制Queue中元素到数组里面去
public void clear()先 lock.lock() ,把数组内元素设置为 null
public int drainTo(Collection<? super E> c)把this的元素搬运到另一个Collection c
public int drainTo(Collection<? super E> c, int maxElements)把this的元素搬运到另一个Collection c
public Iterator iterator()获取迭代器 Iterator
private void readObject(java.io.ObjectInputStream s)从输入流读取数据
没有 writeObject方法因为上面的变量都不是 transient ,也就是不用特殊处理
6.3 Demo
  • 代码:
    private static void test04() throws Exception{
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue(2);

        new Thread(() -> {
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "--- put A");
                arrayBlockingQueue.put("A");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("first take ...");
        System.out.println(arrayBlockingQueue.take());
        System.out.println("end take ...");
    }
  • 输出:
first take ...
Thread-0--- put A
A
end take ...
  • 流程
  1. 先 take 阻塞住
  2. put进去一个元素
  3. take成功拿到

=== 点击查看top目录 ===

6.4 ArrayBlockingQueue 与 LinkedBlockingQueue区别
  1. 一个内部是数组(双index),一个内部是链表(单向)
  2. ArrayBlockingQueue 的读写公用一个 lock,LinkedBlockingQueue 有两个lock,能真正做到同时读写。
  3. ArrayBlockingQueue 用 int 记录数量,LinkedBlockingQueue 采用 AtomicInteger。
  4. ArrayBlockingQueue 容量初始化后不能修改,LinkedBlockingQueue 可以一直扩容。

=== 点击查看top目录 ===

七、 子类 SynchronousQueue

  • 生产1次,消费1次,再生产,再消费,否则堵塞。
  • SynchronousQueue 既继承了 AbstractQueue抽象类又实现了 BlockingQueue,跟 LinkedBlockingQueue 一样
public class SynchronousQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    private static final long serialVersionUID = -3223113410248163686L;
  • Java 6的SynchronousQueue的实现采用了一种性能更好的无锁算法 — 扩展的“Dual stack and Dual queue”算法。
  • 竞争机制支持公平和非公平两种:非公平竞争模式使用的数据结构是后进先出栈(Lifo Stack);公平竞争模式则使用先进先出队列(Fifo Queue),性能上两者是相当的,一般情况下,Fifo通常可以支持更大的吞吐量,但Lifo可以更大程度的保持线程的本地化。

=== 点击查看top目录 ===

7.1 变量
变量备注
static final int NCPUS = Runtime.getRuntime().availableProcessors();可用CPU数量
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;如果可用cpu是1 ,spin 0次
static final int maxUntimedSpins = maxTimedSpins * 16;
static final long spinForTimeoutThreshold = 1000L;spin 上限阈值1000
private transient volatile Transferer transferer;内部类 Transferer
private ReentrantLock qlock;对象序列化的时候用到
private WaitQueue waitingProducers;对象序列化的时候用到
private WaitQueue waitingConsumers;对象序列化的时候用到

=== 点击查看top目录 ===

7.2 方法
  • 主要使用的方法
方法备注
public void put(E e) throws InterruptedException不会 timeout ,一直阻塞尝试塞入
public boolean offer(E e, long timeout, TimeUnit unit)会 timeout,尝试塞入 ,失败返回 false
public boolean offer(E e)会 timeout,尝试塞入 ,一次失败返回 false
public E take() throws InterruptedException不会 timeout ,一直阻塞
public E poll(long timeout, TimeUnit unit) throws InterruptedException会 timeout ,取不到就返回false
public E poll()会 timeout ,一次取不到就返回false

=== 点击查看top目录 ===

  • 特殊处理方法(由于Queue内最多已有一个Element)
方法备注
public boolean isEmpty()return true;
public int size()return 0;
public int remainingCapacity()return 0;
public void clear(){}
public boolean contains(Object o)return false;
public boolean remove(Object o)return false;
public boolean containsAll(Collection<?> c)return c.isEmpty(); c是empty就返回true,其余都是false
public boolean removeAll(Collection<?> c)return false;
public boolean retainAll(Collection<?> c)return false;
public E peek()return null; 窥探一下都不给
public Iterator iterator()return Collections.emptyIterator();
public Spliterator spliterator()return Spliterators.emptySpliterator();
public Object[] toArray()return new Object[0];
public T[] toArray(T[] a)if (a.length > 0) a[0] = null; return a;
public int drainTo(Collection<? super E> c)倒水,poll 最多一个
public int drainTo(Collection<? super E> c, int maxElements)倒水,poll 最多一个
private void writeObject(java.io.ObjectOutputStream s)
private void readObject(java.io.ObjectInputStream s)

=== 点击查看top目录 ===

7.3 Demo
public static void main(String[] args) throws Exception{
        // 如果为 true566,则等待线程以 FIFO 的顺序竞争访问;否则顺序是未指定的。
        // SynchronousQueue<Integer> sc = new SynchronousQueue<>(true);//fair
        SynchronousQueue<Integer> sc = new SynchronousQueue<>(); // 默认不指定的话是false,不公平的
        new Thread(() ->{ // 生产者线程
            while (true){
                try {
                    //将指定元素添加到此队列,如有必要则等待另一个线程接收它。
//                    sc.put(new Random().nextInt(50));
                    // 如果另一个线程正在等待以便接收指定元素,则将指定元素插入到此队列。如果没有等待接受数据的线程则直接返回false
//                     System.out.println("sc.offer(new Random().nextInt(50)): "+sc.offer(new Random().nextInt(50)));
                    //如果没有等待的线程,则等待指定的时间。在等待时间还没有接受数据的线程的话,直接返回false
                    int temp = new Random().nextInt(50);
                    Thread.sleep(500);
                    System.out.println("put : " + temp +  ", 操作运行完毕..." ); //是操作完毕,并不是添加或获取元素成功!
                    sc.offer(temp,5,TimeUnit.SECONDS);


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(1000);

        new Thread(() ->{ //消费者线程。
            while (true){
                try {
                    // 是操作完毕,并不是添加或者获取元素成功!
                    System.out.println("----------------> sc.take: " + sc.take() + ",获取操作运行完毕..");
                    Thread.sleep(1000);


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
  • 输出:
----------------> sc.take: 20,获取操作运行完毕..
put : 20, 操作运行完毕...
----------------> sc.take: 32,获取操作运行完毕..
put : 32, 操作运行完毕...
----------------> sc.take: 48,获取操作运行完毕..
put : 48, 操作运行完毕...
----------------> sc.take: 44,获取操作运行完毕..
put : 44, 操作运行完毕...
put : 18, 操作运行完毕...
----------------> sc.take: 18,获取操作运行完毕..
----------------> sc.take: 12,获取操作运行完毕..
put : 12, 操作运行完毕...
  • 结论:
  1. 生产一个消费一个
  2. queue必须空才能put成功,queue必须满才能take成功

=== 点击查看top目录 ===

7.4 实战 Executors
7.4.1 Executors#newCachedThreadPool
  • 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 (线程可复用)
  • 内部使用 SynchronousQueue,也就是生产一个Runnable,消费一个Runnable,这个后续讲解
  • java.util.concurrent.Executors#newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
  • java.util.concurrent.Executors#newCachedThreadPool(java.util.concurrent.ThreadFactory)
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);

=== 点击查看top目录 ===

八、 子类 PriorityBlockingQueue

  • 底层是数组
  • 优先级队列,优先级高的,先被 take 出来
  • 实现Comparable接口,put的时候进行排序
  • 比较规则:当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1
  • 优先级队列不允许null值,不允许未实现Comparable接口的对象。
8.1 demo01(不允许未实现Comparable接口的对象)
public static void main(String[] args) throws InterruptedException {
     PriorityBlockingQueue<A> queue2 = new PriorityBlockingQueue<>();
     queue2.put(new A());
 }
private static class A{}
  • 输出:
Exception in thread "main" java.lang.ClassCastException: indi.sword.util.concurrent._21_03_TestBlockingQueue_PriorityBlockingQueue$A cannot be cast to java.lang.Comparable
	at java.util.concurrent.PriorityBlockingQueue.siftUpComparable(PriorityBlockingQueue.java:358)
	at java.util.concurrent.PriorityBlockingQueue.offer(PriorityBlockingQueue.java:490)
	at java.util.concurrent.PriorityBlockingQueue.put(PriorityBlockingQueue.java:512)
	at indi.sword.util.concurrent._21_03_TestBlockingQueue_PriorityBlockingQueue.main(_21_03_TestBlockingQueue_PriorityBlockingQueue.java:28)
  • 结论:抛异常,队内元素必须实现Comparable。

=== 点击查看top目录 ===

8.2 demo02
public static void main(String[] args) throws InterruptedException {
        PriorityBlockingQueue<PriorityElement> queue = new PriorityBlockingQueue<>();

        for (int i = 0; i < 5; i++) {
            Random random = new Random();
            PriorityElement element = new PriorityElement(random.nextInt(10));
            queue.put(element);
        }
        while (!queue.isEmpty()){
            System.out.println(queue.take());
        }
    }
  • 内部对象
@Data
@ToString
class PriorityElement implements Comparable<PriorityElement>{
    private int priority;// 定义优先级

    public PriorityElement(int priority){
        // 初始化优先级
        this.priority = priority;
    }

    @Override
    public int compareTo(PriorityElement o) {
        //按照优先级大小进行排序
        return this.getPriority() >= o.getPriority() ? -1: 1;
    }
}
  • 输出:
PriorityElement(priority=5)
PriorityElement(priority=4)
PriorityElement(priority=2)
PriorityElement(priority=1)
PriorityElement(priority=0)

=== 点击查看top目录 ===

九、 子类 DelayQueue

  • 支持延时获取元素的无界阻塞队列
  • 应用场景:
  1. 缓存系统的设计:使用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,就表示有缓存到期了。
  2. 定时任务调度:使用DelayQueue保存当天要执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,比如Timer就是使用DelayQueue实现的。

DelayQueue阻塞的是其内部元素,DelayQueue中的元素必须实现 java.util.concurrent.Delayed接口
getDelay()方法的返回值就是队列元素被释放前的保持时间,如果返回0或者一个负值,就意味着该元素已经到期需要被释放,此时DelayedQueue会通过其take()方法释放此对象。
从 Delayed 接口定义可以看到,它还继承了Comparable接口,这是因为DelayedQueue中的元素需要进行排序,一般情况,我们都是按元素过期时间的优先级进行排序。

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {
9.1 Demo
public class _21_01_TestBlockingQueue_DelayQueue {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<DelayedElement> queue = new DelayQueue<>();
        DelayedElement ele = new DelayedElement("cache 3 seconds",3000);
        queue.put(ele);
        System.out.println("has put into queue ...");
        System.out.println(queue.take());
    }
}


@ToString
class DelayedElement implements Delayed{
    private long expired;
    private long delay;
    private String name;

    public DelayedElement(String elementName,long delay){
        this.name = elementName;
        this.delay = delay;
        expired = (delay + System.currentTimeMillis());
    }

    @Override
    public int compareTo(Delayed o) {
        DelayedElement cached = (DelayedElement)o;

        return cached.getExpired() > this.getExpired() ? 1: -1;
    }

    @Override
    public long getDelay(TimeUnit unit) {

        return ( this.getExpired() -  System.currentTimeMillis());
    }

    public long getExpired() {
        return expired;
    }
}
  • 输出:
has put into queue ...
DelayedElement(expired=1571302866874, delay=3000, name=cache 3 seconds)

=== 点击查看top目录 ===

9.2 Demo2
public class _21_02_TestBlockingQueue_DelayQueue {
    static final int STUDENT_SIZE = 30;
    public static void main(String[] args) throws InterruptedException {
        Random r = new Random();

        // 所有学生看做一个延迟队列
        DelayQueue<Student> students = new DelayQueue<>();

        // 构造一个线程池用来让学生们“做作业”
        ExecutorService executorService = Executors.newFixedThreadPool(STUDENT_SIZE);
        for (int i = 0; i < STUDENT_SIZE; i++) {
            students.put(new Student("学生" + (i+1),3000 + r.nextInt(10000)));
        }
        // 开始做题
        while(! students.isEmpty()){
            executorService.execute(students.take());
        }
        executorService.shutdown();
    }
}


class Student implements Runnable,Delayed{

    private String name; // 学生姓名
    private long costTime;// 该学生做题的时间,假设根据能力,我们事先推算出来的
    private long finishedTime; // 完成时间 costTime + System.currentTimeMills()

    public Student(String name,long costTime){
        this.name = name;
        this.costTime = costTime;
        finishedTime = costTime + System.currentTimeMillis();
    }

    @Override
    public void run() {
        System.out.println(name + "交卷,用时: " + costTime / 1000 + "s");
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return (finishedTime - System.currentTimeMillis());
    }

    @Override
    public int compareTo(Delayed o) {
        Student other = (Student)o;
        return costTime >= other.costTime ? 1: -1;
    }
}
  • 输出:
学生17交卷,用时: 3s
学生2交卷,用时: 3s
学生3交卷,用时: 3s
学生16交卷,用时: 3s
学生30交卷,用时: 3s
学生22交卷,用时: 5s
学生29交卷,用时: 5s
学生25交卷,用时: 5s
学生18交卷,用时: 6s
学生28交卷,用时: 6s
学生14交卷,用时: 6s
学生23交卷,用时: 6s
学生19交卷,用时: 7s
学生11交卷,用时: 7s
学生9交卷,用时: 7s
学生27交卷,用时: 7s
学生7交卷,用时: 7s
学生15交卷,用时: 7s
学生26交卷,用时: 7s
学生8交卷,用时: 8s
学生4交卷,用时: 9s
学生1交卷,用时: 9s
学生24交卷,用时: 10s
学生20交卷,用时: 10s
学生6交卷,用时: 10s
学生10交卷,用时: 11s
学生21交卷,用时: 11s
学生12交卷,用时: 12s
学生13交卷,用时: 12s
学生5交卷,用时: 12s

=== 点击查看top目录 ===


十、番外篇

下一章节::【Java Collection】子类 SynchronousQueue 图解剖析(五)
上一章节:【Java Collection】常见 List 子类剖析(三)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值