资讯专栏INFORMATION COLUMN

Java延迟队列DelayQueue的使用:多考生考试模拟

Awbeci / 2015人阅读

摘要:如果延迟都还没有期满,则队列没有头部,此时调用将直接返回,调用将会发生阻塞,直到有元素发生到期,才会返回。比如,此元素还有毫秒延时期满传入的参数为那么返回值为,即两秒。

DelayQueue简介

DelayQueue是juc包中的类,它表示的是一个无界的延迟队列,定义如下:

public class DelayQueue extends AbstractQueue implements BlockingQueue;

DelayQueue存储的元素需要实现Delayed接口以实现优先级比较和延时取得。

DelayQueue还是一个阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,此时调用 poll() 将直接返回 null,调用 take() 将会发生阻塞,直到有元素发生到期,take() 才会返回。

当一个元素的 getDelay() 方法返回一个小于等于 0 的值时,将发生到期。

场景应用

下面将使用此类实现一个多考生考试的场景:

考试总时间为10秒,至少2秒后才可进行交卷。

考生可在2-10秒这段时间内的任意时间交卷。

考试时间一到,所有未交卷的学生必须交卷。

注:上述时间数据仅为测试方便使用,可根据实际情况进行修改

使用enum定义出时间常量:

    enum Times {
        SUMMIT_TIME(10), //考试总时间
        SUMBMIT_LIMIT(2), // 交卷限制时间
        MAX_RAND_TIME(15); // 模拟考生所需最大时间
        private final int value;
    
        private Times(int value) {
            this.value = value;
        }
    
        public int getValue() {
            return value;
        }
    }
定义学生类

基本定义:

    class Student implements Delayed {
        private String name;
        private long delay; // 考试花费时间,单位为毫秒
        private long expire; // 交卷时间,单位为毫秒
        
        // 此构造可随机生成考试花费时间
        public Student(String name) { 
            this.name = name;
            this.delay = TimeUnit.MILLISECONDS.convert(getRandomSeconds(), TimeUnit.SECONDS); 
            this.expire = System.currentTimeMillis() + this.delay;
        }
        
        //此构造可指定考试花费时间
        public Student(String name, long delay, TimeUnit unit) {
            this.name = name;
            this.delay = TimeUnit.MILLISECONDS.convert(delay, unit);
            this.expire = System.currentTimeMillis() + this.delay;
        }
        // ...
    }

利用Random获取学生考试花费时间:

    public int getRandomSeconds() { // 获取随机花费时间,范围:2-10秒
        return new Random().nextInt(Times.MAX_RAND_TIME.getValue() - Times.SUMBMIT_LIMIT.getValue())
                    + Times.SUMBMIT_LIMIT.getValue();
    }

覆写的compareTo()和getDelay()方法:

其中,getDelay()方法根据传入的TimeUnit返回剩余延时。比如,此元素还有2000毫秒延时期满、传入的参数为TimeUnit.SECONDS,那么返回值为2,即两秒。

    @Override
    public int compareTo(Delayed o) { // 此方法的实现用于定义优先级
        long td = this.getDelay(TimeUnit.MILLISECONDS);
        long od = o.getDelay(TimeUnit.MILLISECONDS);
        return td > od ? 1 : td == od ? 0 : -1;
    }

    @Override
    public long getDelay(TimeUnit unit) { // 这里返回的是剩余延时,当延时为0时,此元素延时期满,可从take()取出
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }
主方法实现

初始化对象

DelayQueue queue = new DelayQueue<>();

添加测试数据

queue.add(new Student("范冰冰"));
queue.add(new Student("成  龙"));
queue.add(new Student("李一桐"));
queue.add(new Student("宋小宝"));
queue.add(new Student("吴  京"));
queue.add(new Student("绿巨人"));
queue.add(new Student("洪金宝"));
queue.add(new Student("李云龙"));
queue.add(new Student("钢铁侠"));
queue.add(new Student("刘德华"));
queue.add(new Student("戴安娜"));

添加一条用于考试结束时强制交卷的属性

queue.add(new Student("submit", Times.SUBMIT_TIME.getValue(),TimeUnit.SECONDS));

开始考试

while (true) {
    Student s = queue.take(); // 必要时进行阻塞等待
    if (s.getName().equals("submit")) {
        System.out.println("时间已到,全部交卷!");
        // 利用Java8 Stream特性使尚未交卷学生交卷
        queue.parallelStream()
             .filter(v -> v.getExpire() >= s.getExpire())
             .map(Student::submit)
             .forEach(System.out::println);
        System.exit(0);
    }
    System.out.println(s);
}

输出样例

完整代码
package cn.gss.juc;

import java.text.DateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

enum Times {
    SUBMIT_TIME(10), SUMBMIT_LIMIT(2), MAX_RAND_TIME(15);
    private final int value;

    private Times(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
/**
 * DelayQueue实现多考生考试
 * @author Gss
 */
public class TestDelayedQueue {

    public static void main(String[] args) throws InterruptedException {
        DelayQueue queue = new DelayQueue<>();
        queue.add(new Student("范冰冰"));
        queue.add(new Student("成  龙"));
        queue.add(new Student("李一桐"));
        queue.add(new Student("宋小宝"));
        queue.add(new Student("吴  京"));
        queue.add(new Student("绿巨人"));
        queue.add(new Student("洪金宝"));
        queue.add(new Student("李云龙"));
        queue.add(new Student("钢铁侠"));
        queue.add(new Student("刘德华"));
        queue.add(new Student("戴安娜"));
        queue.add(new Student("submit", Times.SUBMIT_TIME.getValue(), TimeUnit.SECONDS));
        while (true) {
            Student s = queue.take(); // 必要时进行阻塞等待
            if (s.getName().equals("submit")) {
                System.out.println("时间已到,全部交卷!");
                // 利用Java8 Stream使尚未交卷学生交卷
                queue.parallelStream()
                     .filter(v -> v.getExpire() >= s.getExpire())
                     .map(Student::submit)
                     .forEach(System.out::println);
                System.exit(0);
            }
            System.out.println(s);
        }
    }

}

class Student implements Delayed {
    private String name;
    private long delay; // 考试花费时间,单位为毫秒
    private long expire; // 交卷时间,单位为毫秒

    // 此构造可随机生成考试花费时间
    public Student(String name) {
        this.name = name;
        this.delay = TimeUnit.MILLISECONDS.convert(getRandomSeconds(), TimeUnit.SECONDS); // 随机生成考试花费时间
        this.expire = System.currentTimeMillis() + this.delay;
    }

    // 此构造可指定考试花费时间
    public Student(String name, long delay, TimeUnit unit) {
        this.name = name;
        this.delay = TimeUnit.MILLISECONDS.convert(delay, unit);
        this.expire = System.currentTimeMillis() + this.delay;
    }

    public int getRandomSeconds() { // 获取随机花费时间
        return new Random().nextInt(Times.MAX_RAND_TIME.getValue() - Times.SUMBMIT_LIMIT.getValue())
                + Times.SUMBMIT_LIMIT.getValue();
    }

    public Student submit() { // 设置花费时间和交卷时间,考试时间结束强制交卷时调用此方法
        setDelay(Times.SUBMIT_TIME.getValue(), TimeUnit.SECONDS);
        setExpire(System.currentTimeMillis());
        return this;
    }

    public String getName() {
        return name;
    }

    public long getExpire() {
        return expire;
    }

    public void setDelay(long delay, TimeUnit unit) {
        this.delay = TimeUnit.MILLISECONDS.convert(delay, TimeUnit.SECONDS);
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }

    @Override
    public int compareTo(Delayed o) { // 此方法的实现用于定义优先级
        long td = this.getDelay(TimeUnit.MILLISECONDS);
        long od = o.getDelay(TimeUnit.MILLISECONDS);
        return td > od ? 1 : td == od ? 0 : -1;
    }

    @Override
    public long getDelay(TimeUnit unit) { // 这里返回的是剩余延时,当延时为0时,此元素延时期满,可从take()取出
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public String toString() {
        return "学生姓名:" + this.name + ",考试用时:" + TimeUnit.SECONDS.convert(delay, TimeUnit.MILLISECONDS) + ",交卷时间:"
                + DateFormat.getDateTimeInstance().format(new Date(this.expire));
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70500.html

相关文章

  • Java简易定时任务实现

    摘要:线程池任务队列构造方法,实例化时启动线程设置任务队列,用于任务重新入队任务入队从延迟队列中获取任务利用线程池执行任务实现了接口,执行实际的业务并决定任务是否重新进入延迟队列。 前言 接入微信支付的时候,看到微信支付的回调是按照某种频率去回调的,像15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h这样,其中有一次成功就不会再回调。于...

    hqman 评论0 收藏0
  • 周期性线程池与主要源码解析

    摘要:今天给大家介绍下周期性线程池的使用和重点源码剖析。用来处理延时任务或定时任务定时线程池类的类结构图接收类型的任务,是线程池调度任务的最小单位。周期性线程池任务的提交方式周期性有三种提交的方式。 之前学习ThreadPool的使用以及源码剖析,并且从面试的角度去介绍知识点的解答。今天给大家介绍下周期性线程池的使用和重点源码剖析。 ScheduledThreadPoolExecutor S...

    马龙驹 评论0 收藏0
  • 【2022考研最后40天】要注意这4个时间节点和这5件事情

    摘要:请考生务必妥善保管个人网报用户名密码及准考证居民身份证等证件,避免泄露丢失造成损失。自主划线院校会在月陆续公布初试成绩基本要求。锁定时间到达后,如招生单位未明确受理意见,锁定解除,考生可继续填报其他志愿。 ...

    jaysun 评论0 收藏0
  • 有什么行为习惯昭示着你是个编程大佬?

    摘要:怎样让程序代码行更少,怎样用更严谨的逻辑减少错误发生,怎样让用户体验更好,并不断学习,着手解决现有的问题,这就是编程大佬的日常。认证报名不受年龄职业学历等限制,可根据自己的能力情况从到级别进行选择测评考试。 自查 自学 自省 计算机行业永远没有全知全能一说,开发者的水平再高,也不可能躺在功劳...

    Taste 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<