阻塞队列-DelayQueue

DelayQueue延迟队列

基本使用

  1. DelayQueue存储的元素必须实现Delayed接口
  2. 重写getDelay和compareTo方法
  3. getDelay返回小于等于0时候弹出元素
  4. compareTo方法用于堆中元素移动时候的比较
public class DelayQueueTest {
    public static void main(String[] args) throws InterruptedException {
        Task taskA = new Task("taskA", 6000L);
        Task taskB = new Task("taskB", 3000L);
        Task taskC = new Task("taskC", 4000L);
        Task taskD = new Task("taskD", 5000L);
        DelayQueue<Task> queue = new DelayQueue<>();
        queue.put(taskA);
        queue.put(taskB);
        queue.put(taskC);
        queue.put(taskD);

        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
    }
    static class Task implements Delayed{
        //任务名称
        private String name;
        //任务执行时间
        private Long time;

        public Task(String name, Long delay) {
            this.name = name;
            this.time = TimeUnit.MILLISECONDS.convert(System.currentTimeMillis() + delay,TimeUnit.MILLISECONDS);
        }

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

        @Override
        public int compareTo(Delayed o) {
            return (int) (this.time - ((Task)o).getTime());
        }

        public String getName() {
            return name;
        }

        public Long getTime() {
            return time;
        }

        @Override
        public String toString() {
            return "Task{" +
                    "name='" + name + '\'' +
                    ", time=" + time +
                    '}';
        }
    }
}

源码分析

成员变量

    //锁,添加、移除元素公用一把锁
    private final transient ReentrantLock lock = new ReentrantLock();
    //存储元素使用优先级队列
    private final PriorityQueue<E> q = new PriorityQueue<E>();
    //存储正在等待元素的线程
    private Thread leader;
    //用于线程挂起、唤醒
    private final Condition available = lock.newCondition();

构造方法

    public DelayQueue() {}
    public DelayQueue(Collection<? extends E> c) {
        this.addAll(c);
    }

添加元素相关方法

add(E e)

调用offer方法

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

调用offer方法

  public void put(E e) {
        offer(e);
    }
offer(E e, long timeout, TimeUnit unit)

延迟队列是基于优先级队列实现,添加元素不存在阻塞情况,直接调用offer方法

   public boolean offer(E e, long timeout, TimeUnit unit) {
        return offer(e);
    }
offer(E e)

offer方法分析

    public boolean offer(E e) {
        //拿到锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //调用优先级队列的offer方法弹出堆顶元素
            q.offer(e);
            //如果不存在元素,返回null
            if (q.peek() == e) {
                leader = null;
                //这里是唤醒等待获取元素的线程
                available.signal();
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

获取元素相关方法

remove(Object o)

调用优先级队列中的remove方法,返回堆顶元素

   public boolean remove(Object o) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return q.remove(o);
        } finally {
            lock.unlock();
        }
    }
poll()
    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //拿到堆顶元素
            E first = q.peek();
            //如果堆顶元素为null或者堆顶元素的延迟时间未到返回空,否则调用priorityQueue中的poll方法返回堆顶元素
            return (first == null || first.getDelay(NANOSECONDS) > 0)
                ? null
                : q.poll();
        } finally {
            lock.unlock();
        }
    }
poll(long timeout, TimeUnit unit)
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //死循环,直到获取元素或者指定时间到循环结束
            for (;;) {
                E first = q.peek();
                //如果元素为空
                if (first == null) {
                    //等待时间到了,返回null
                    if (nanos <= 0L)
                        return null;
                    else
                        //否则挂起线程,等待生产者唤醒,即offer中的唤醒操作
                        nanos = available.awaitNanos(nanos);
                } else {
                    //到这儿,证明有元素,拿到堆顶元素距离可以获取的时间,纳秒值
                    long delay = first.getDelay(NANOSECONDS);
                    if (delay <= 0L)
                        //如果元素时间到了,直接返回
                        return q.poll();
                    //如果指定等待时间到了,返回null
                    if (nanos <= 0L)
                        return null;
                    //防止线程挂起后持有对象引用
                    first = null; // don't retain ref while waiting
                    //如果指定等待时间小于元素延迟时间或者有线程等待获取栈顶元素,挂起线程
                    if (nanos < delay || leader != null)
                        nanos = available.awaitNanos(nanos);
                    else {
                        //到这儿,证明没有线程等待获取堆顶元素且指定等待时间比元素时间大,在等待时间内可以获取元素
                        //否则将正在等待获取元素的线程设为当前线程
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            //线程挂起时间设置为元素的延迟时间
                            //注意:当前线程虽然阻塞时间为元素延迟时间,但当前线程不一定能拿到堆顶元素,因为阻塞结束会将leader设置为null,另外生产者如果发现堆顶元素变了也可以将lead设置为null,这会导致leader被设置为其它线程
                            long timeLeft = available.awaitNanos(delay);
                            nanos -= delay - timeLeft;
                        } finally {
                            //判断是否是等待元素的线程,如果是,将等待线程设为null
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            //每个线程到这需要判断有没有线程在等待元素,如果没有且元素不为null,唤醒等待的线程
            if (leader == null && q.peek() != null)
                //唤醒
                available.signal();
            lock.unlock();
        }
    }
take()

除非线程被打断,否则一直等到获取元素

   public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //死循环,直到拿到元素
            for (;;) {
                //拿到堆顶元素
                E first = q.peek();
                //如果为null,挂起线程,等待被生产者唤醒
                if (first == null)
                    available.await();
                else {
                    //元素不为null,拿到堆顶元素的延迟时间
                    long delay = first.getDelay(NANOSECONDS);
                    //如果元素延迟时间到了,直接返回
                    if (delay <= 0L)
                        return q.poll();
                    first = null; // don't retain ref while waiting
                    //如果元素延迟时间没到,且有其它线程正在等待元素,挂起线程
                    if (leader != null)
                        available.await();
                    else {
                        //如果没有线程等待元素,将当前线程设置为正等待元素的线程
                        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();
        }
    }
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值