DelayQueue学习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ko0491/article/details/90905541

DelayQueue 并发队列是 一个无界阻塞延迟队 列 ,队列中的每个元素都有个过期时 间,
当从队列获取元素时,只有过期元素才会出队列。队列头元素是最快要过期的元素。
在这里插入图片描述

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {

    private final transient ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue<E> q = new PriorityQueue<E>();
     private Thread leader = null;
    private final Condition available = lock.newCondition();

    public DelayQueue() {}


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


offer操作

    public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            q.offer(e);
            if (q.peek() == e) {
                leader = null;
                available.signal();
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

代码首先获取独占锁,然后添加元素到优先级队列,由于 q 是优先级队列,所以
添加元素后,调用 q.peek() 方法返回的并不一定是当前添加的元素。如果代码( 2 )判断
结果为 true,则说明当前元素 e 是最先将过期的 , 那么重置 leader 线程为 null , 这时候激
活 avaliable 变量条件队列里面的一个线程,告诉它队列里面有元素了.

take 操作

获取并移除队列里面延迟时间过期的元素,如果队列里面没有过期元素则等待


 public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
            //1 获取但不移除队首元素
                E first = q.peek();
                if (first == null)
                //2 阻塞 进入等待
                    available.await();
                else {
                    long delay = first.getDelay(NANOSECONDS);
                    //3 时间小于=于0
                    if (delay <= 0)
                        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();
        }
    }

首先获取独占锁 lock 。 假设线程 A 第一次调用 队列的 take方法时队列为
空 ,则 执行代码1后 first==null ,所以会执行代码。 把当前线程放入 available 的条
件队列里阻塞等待。

当有另 外一个线程 B 执行 offer ( item )方法并且添加元素到队列时 , 假设此时没有其
他线程执行入队操作 ,则线程 B 添加 的元素是队首元素 , 那么执行 q.peek()

线程 A 被撤活并循环后重新获取 队首元素,这时候 first 就是线程 B 新增的元素,可
知这时候 first 不为 null , 则调用 first. getDelay(TimeUnit.NANOSECONDS) 方法查看该元素
还剩余多 少时间就要过期,如 果 de lay<=O 则说明 己经过期,那么直接出队返回。否则查
看 leader 是否为 null ,不为 null 则说明其他线程也在执行 take ,则把该线程放入条件队列。
如果这时候 leader 为 null,则 :izt取当前线程 A 为 leader 线程 , 然后执行代码(5 ) 等待 delay
时- 间(这期间该线程会释放锁,所以其他线程可 以 offer 添加元素,也可以 take 阻塞自己)

剩余过期时间到后,线程 A 会重新竞争得到锁,然后重置 leader 线程为 nu ll , 重新进入循
环,这时候就会发现队头的元素己经过期了,则会直接返回队头元素 。
在返回前会执行 finally 块里面的代码( 7 ),代码 (7 )执行结果为 true 则说明 当前线
程从队列移除过期元素后,又有其他线程执行了入队操作,那么这时候调用条件变量的
singa l 方法,激活条件队列里面的等待线程。

poll操作

获取并移除队头过期元素,如果没有过期元素则返回 null

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = q.peek();
            //如果队列为空,或者不为空但是队头元素没有过期则返回 null
            if (first == null || first.getDelay(NANOSECONDS) > 0)
                return null;
            else
                return q.poll();
        } finally {
            lock.unlock();
        }
    }

size 操作



    public int size() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return q.size();
        } finally {
            lock.unlock();
        }
    }

首先获取独占锁,然后调用优先级队列 的 size 方法

demo


package com.ghgcn.thread.lesson07.concurrentqueue;

import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @author 刘楠
 * @since 2019/6/5
 */
public class DelayQueueTest01 {

    static  class DelayedEle implements Delayed{
        private final long delayTime ; //延迟时间
        private final long expire ; //到期时间
        private String taskName ; //任务名称

        public DelayedEle(long delay, String taskName) {
            this.delayTime = delay;

            this.taskName = taskName;
            this.expire  = System.currentTimeMillis() +delay;
        }


        /**
         * 剩余时间=到期时间 - 当前时间
         * @param unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert( expire-System.currentTimeMillis(),TimeUnit.MILLISECONDS);
        }

        /**
         * 优先级队列里面的优先级规则
         * @param o
         * @return
         */
        @Override
        public int compareTo(Delayed o) {
          return (int) (this.getDelay(TimeUnit.MILLISECONDS)- o.getDelay(TimeUnit.MILLISECONDS));
        }


        @Override
        public String toString() {
            return "DelayedEle{" +
                    "delayTime=" + delayTime +
                    ", expire=" + expire +
                    ", taskName='" + taskName + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        //创建队列
        DelayQueue<DelayedEle> queue =new DelayQueue<DelayedEle>();

        ThreadLocalRandom random =ThreadLocalRandom.current();

        for(int i = 0; i<10; i++){
            DelayedEle ele = new DelayedEle(random.nextInt(1000),"task: "+i);
            queue.offer(ele);
        }

        //取出
        DelayedEle delayedEle=null;
        for(;;){
            try {
                while ((delayedEle=queue.take())!=null){
                    System.err.println(delayedEle.toString());
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        }
    }
}

  • 结果

DelayedEle{delayTime=92, expire=1559727104677, taskName=‘task: 8’}
DelayedEle{delayTime=126, expire=1559727104711, taskName=‘task: 3’}
DelayedEle{delayTime=363, expire=1559727104948, taskName=‘task: 7’}
DelayedEle{delayTime=461, expire=1559727105044, taskName=‘task: 1’}
DelayedEle{delayTime=583, expire=1559727105168, taskName=‘task: 5’}
DelayedEle{delayTime=700, expire=1559727105285, taskName=‘task: 4’}
DelayedEle{delayTime=826, expire=1559727105409, taskName=‘task: 0’}
DelayedEle{delayTime=846, expire=1559727105431, taskName=‘task: 9’}
DelayedEle{delayTime=983, expire=1559727105568, taskName=‘task: 6’}
DelayedEle{delayTime=999, expire=1559727105584, taskName=‘task: 2’}

创建延迟任务 DelayedEle 类 , 其中 delayTime 表示当前任务需要延
迟多少 ms 时间过期, expire 则 是当前时 间的 ms 值加上 delayTime 的值 。另 外,实现了
Delayed 接口,实现了 long getDelay(TimeUnit unit) 方法用来获取当前元素还剩下多少时间过期,实现了 int compareTo(Delayed o)方法用来决定优先级队列元素的 比较规则 。

DelayQueue 队列其内部使用 PriorityQueue 存放数据,使
用 ReentrantLock 实现线程同步 。另 外队列里面的 元素要实现 Delayed 接口,其中一个是
获取当前元素到过期时间剩余时间的接 口,在出队时判断元素是否过期 了,一个是元素之
间比较的接口 ,因为这是一个有优先级的队列 。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ko0491/article/details/90905541
今日推荐