【Java】3.DelayQueue使用与分析

来源:互联网 发布:python 绘制分布图 编辑:程序博客网 时间:2024/06/05 20:02
1.官方资料:

DelayQueue class implements the BlockingQueue interface. Read the BlockingQueue text for more information about the interface.

The DelayQueue blocks the elements internally until a certain delay has expired. The elements must implement the interface java.util.concurrent.Delayed. Here is how the interface looks:

public interface Delayed extends Comparable<Delayed< { public long getDelay(TimeUnit timeUnit);}

The value returned by the getDelay() method should be the delay remaining before this element can be released. If 0 or a negative value is returned, the delay will be considered expired, and the element released at the next take() etc. call on the DelayQueue.

The TimeUnit instance passed to the getDelay() method is an Enum that tells which time unit the delay should be returned in. The TimeUnit enum can take these values:

DAYSHOURSMINUTESSECONDSMILLISECONDSMICROSECONDSNANOSECONDS

The Delayed interface also extends the java.lang.Comparable interface, as you can see, which means that Delayed objects can be compared to each other. This is probably used internally in the DelayQueue to order the elements in the queue, so they are released ordered by their expiration time.

Here is an example of how to use the DelayQueue:

package com.naton.current;import java.util.concurrent.DelayQueue;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;public class DelayQueueExample {    public static void main(String[] args) {        DelayQueue queue = new DelayQueue();        Delayed element1 = new DelayedElement();        queue.put(element1);        try {            Delayed element2 = queue.take();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    static class DelayedElement implements Delayed{        @Override        public long getDelay(TimeUnit unit) {            return unit.convert( time * 1000L - System.currentTimeMillis(), TimeUnit.MILLISECONDS );          }        @Override        public int compareTo(Delayed o) {            return 0;        }    }}

The DelayedElement is an implementation of the Delayed interface that I have created. It is not part of the java.util.concurrent package. You will have to create your own implementation of the Delayed interface to use the DelayQueue class.

2.源码部分:
package java.util.concurrent;import static java.util.concurrent.TimeUnit.NANOSECONDS;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;import java.util.*;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() {}    elayQueue(Collection<? extends E> c) {        this.addAll(c);    }        public boolean add(E e) {        return offer(e);    }       /*执行加锁操作    *把元素添加到优先级队列中    *查看元素是否为队首    *如果是队首的话,设置leader为空,唤醒所有等待的队列    释放锁     */    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();        }    }        public void put(E e) {        offer(e);    }        public boolean offer(E e, long timeout, TimeUnit unit) {        return offer(e);    }        public E poll() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            E first = q.peek();            if (first == null || first.getDelay(NANOSECONDS) > 0)                return null;            else                return q.poll();        } finally {            lock.unlock();        }    }<!--0.执行加锁操作1.取出优先级队列元素q的队首2.如果元素q的队首/队列为空,阻塞请求3.如果元素q的队首(first)不为空,获得这个元素的delay时间值4.如果first的延迟delay时间值为0的话,说明该元素已经到了可以使用的时间,调用poll方法弹出该元素,跳出方法5.如果first的延迟delay时间值不为0的话,释放元素first的引用,避免内存泄露6.判断leader元素是否为空,不为空的话阻塞当前线程7.如果leader元素为空的话,把当前线程赋值给leader元素,然后阻塞delay的时间,即等待队首到达可以出队的时间,finally块中释放leader元素的引用8.循环执行从0~7的步骤9.如果leader为空并且优先级队列不为空的情况下(判断还有没有其他后续节点),调用signal通知其他的线程10.执行解锁操作-->    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 <= 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();        }    }        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) {                    if (nanos <= 0)                        return null;                    else                        nanos = available.awaitNanos(nanos);                } else {                    long delay = first.getDelay(NANOSECONDS);                    if (delay <= 0)                        return q.poll();                    if (nanos <= 0)                        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 {                            long timeLeft = available.awaitNanos(delay);                            nanos -= delay - timeLeft;                        } finally {                            if (leader == thisThread)                                leader = null;                        }                    }                }            }        } finally {            if (leader == null && q.peek() != null)                available.signal();            lock.unlock();        }    }       public E peek() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return q.peek();        } finally {            lock.unlock();        }    }    public int size() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return q.size();        } finally {            lock.unlock();        }    }       private E peekExpired() {        // assert lock.isHeldByCurrentThread();        E first = q.peek();        return (first == null || first.getDelay(NANOSECONDS) > 0) ?            null : first;    }        public int drainTo(Collection<? super E> c) {        if (c == null)            throw new NullPointerException();        if (c == this)            throw new IllegalArgumentException();        final ReentrantLock lock = this.lock;        lock.lock();        try {            int n = 0;            for (E e; (e = peekExpired()) != null;) {                c.add(e);       // In this order, in case add() throws.                q.poll();                ++n;            }            return n;        } finally {            lock.unlock();        }    }        public int drainTo(Collection<? super E> c, int maxElements) {        if (c == null)            throw new NullPointerException();        if (c == this)            throw new IllegalArgumentException();        if (maxElements <= 0)            return 0;        final ReentrantLock lock = this.lock;        lock.lock();        try {            int n = 0;            for (E e; n < maxElements && (e = peekExpired()) != null;) {                c.add(e);       // In this order, in case add() throws.                q.poll();                ++n;            }            return n;        } finally {            lock.unlock();        }    }       public void clear() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            q.clear();        } finally {            lock.unlock();        }    }        public int remainingCapacity() {        return Integer.MAX_VALUE;    }        public Object[] toArray() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return q.toArray();        } finally {            lock.unlock();        }    }        public <T> T[] toArray(T[] a) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return q.toArray(a);        } finally {            lock.unlock();        }    }        public boolean remove(Object o) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return q.remove(o);        } finally {            lock.unlock();        }    }        void removeEQ(Object o) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            for (Iterator<E> it = q.iterator(); it.hasNext(); ) {                if (o == it.next()) {                    it.remove();                    break;                }            }        } finally {            lock.unlock();        }    }       public Iterator<E> iterator() {        return new Itr(toArray());    }        private class Itr implements Iterator<E> {        final Object[] array; // Array of all elements        int cursor;           // index of next element to return        int lastRet;          // index of last element, or -1 if no such        Itr(Object[] array) {            lastRet = -1;            this.array = array;        }        public boolean hasNext() {            return cursor < array.length;        }        @SuppressWarnings("unchecked")        public E next() {            if (cursor >= array.length)                throw new NoSuchElementException();            lastRet = cursor;            return (E)array[cursor++];        }        public void remove() {            if (lastRet < 0)                throw new IllegalStateException();            removeEQ(array[lastRet]);            lastRet = -1;        }    }}
3.源码分析

a)无界阻塞队列

b)只有在延迟期满时才能从中提取元素

c)这种队列是有序的,即队头对象的延迟到期时间最长

d)不能将null元素放置到这种队列中。

4.场景使用

我们在开发中,有如下场景

a) 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。

b) 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。

c) 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。

d)session超时管理

e)任务调度系统,能够准确的把握任务的执行时间。

f)订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。

g)订餐通知:下单成功后60s之后给用户发送短信通知。

5.网上一个示例demo
package com.naton.current;import java.util.concurrent.Delayed;import java.util.concurrent.ExecutorService;import java.util.concurrent.TimeUnit;class Student implements Runnable,Delayed{    private String name;    private long submitTime;//交卷时间    private long workTime;//考试时间    public Student() {        // TODO Auto-generated constructor stub    }    public Student(String name, long submitTime) {        super();        this.name = name;        workTime = submitTime;        //都转为转为ns        this.submitTime = TimeUnit.NANOSECONDS.convert(submitTime, TimeUnit.MILLISECONDS) + System.nanoTime();    }    @Override    public void run() {        System.out.println(name + " 交卷,用时" + workTime/100 + "分钟");    }    @Override    public long getDelay(TimeUnit unit) {        return unit.convert(submitTime - System.nanoTime(), unit.NANOSECONDS);    }    @Override    public int compareTo(Delayed o) {        Student that = (Student) o;        return submitTime > that.submitTime?1:(submitTime < that.submitTime ? -1 : 0);    }    public static class EndExam extends Student{        private ExecutorService exec;        public EndExam(int submitTime,ExecutorService exec) {            super(null,submitTime);            this.exec = exec;        }        @Override        public void run() {            exec.shutdownNow();        }    }}package com.naton.current;import java.util.concurrent.DelayQueue;import java.util.concurrent.ExecutorService;class Teacher implements Runnable{    private DelayQueue<Student> students;    private ExecutorService exec;    public Teacher(DelayQueue<Student> students,ExecutorService exec) {        super();        this.students = students;        this.exec = exec;    }    @Override    public void run() {        try {            System.out.println("考试开始……");            while (!Thread.interrupted()) {                students.take().run();            }            System.out.println("考试结束……");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}package com.naton.current;import java.util.Random;import java.util.concurrent.DelayQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 下面的代码模拟一个考试的日子,考试时间为120分钟,30分钟后才可交卷,当时间到了,或学生都交完卷了者考试结束。 */public class Exam {    static final int STUDENT_SIZE = 10;    public static void main(String[] args) {        Random r = new Random();        DelayQueue<Student> students = new DelayQueue<Student>();        ExecutorService exec = Executors.newCachedThreadPool();        /***用来记录最后一个交卷的学生的考试时间,然后就可以结束考试了*/        long shouldEndTime = 0;        for(int i = 0; i < STUDENT_SIZE; i++){            long submitTime = 3000 + r.nextInt(9000);            shouldEndTime = shouldEndTime > submitTime ? shouldEndTime:submitTime;            students.put(new Student("学生" + i, submitTime));        }                shouldEndTime = shouldEndTime > 12000 ? 12000:shouldEndTime;        students.put(new Student.EndExam((int) shouldEndTime,exec));//1200为考试结束时间        exec.execute(new Teacher(students,exec));    }}

示例2:

package com.naton.current;import java.util.concurrent.DelayQueue;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;public class DelayQueueExample {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        DelayQueue<DelayedElement> dq=new DelayQueue<DelayedElement>();        long now = System.currentTimeMillis();        System.out.println("current time in ms:"+ now);        DelayedElement ob1=new DelayedElement("e1", now + 1000);        DelayedElement ob2=new DelayedElement("e2", now + 5000);        DelayedElement ob3=new DelayedElement("e3", now + 1500);        dq.add(ob1);        dq.add(ob2);        dq.add(ob3);        try {            Thread.sleep(1);        } catch (InterruptedException e) {            throw new RuntimeException( e );        }        while(dq.size() > 0){            try {                DelayedElement e = dq.take();                System.out.println("current time in ms:"+ System.currentTimeMillis() +", element:"+ e.name);            } catch (InterruptedException e) {                throw new RuntimeException( e );            }        }    }    static class DelayedElement implements Delayed {        public long time;        public String name;        public DelayedElement(String name, long time){            this.name = name;            this.time = time;        }        @Override        public int compareTo(Delayed o) {            // TODO Auto-generated method stub            if(this.time < ((DelayedElement)o).time) return -1;            else if(this.time > ((DelayedElement)o).time)return 1;            else return 0;        }        @Override        public long getDelay(TimeUnit unit) {            // TODO Auto-generated method stub            long r = unit.convert(time - System.currentTimeMillis(), TimeUnit.NANOSECONDS);            //System.out.println("delay:"+ r);            return r;        }    }}输出结果:current time in ms:1512897117161current time in ms:1512897118169, element:e1current time in ms:1512897118670, element:e3current time in ms:1512897122165, element:e2