DelayedQueue的学习

参考链接:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh

1.DelayedQueue是一个无界的阻塞队列,其内的元素是实现了Delayed接口的元素。

Delayed,一种混合风格的接口,用来标记那些应该在给定延迟时间之后执行的对象。此接口的实现必须定义一个 compareTo 方法,该方法提供与此接口的 getDelay 方法一致的排序。

getDelay的方法返回时剩余的延迟时间。

创建一个Delayed接口的实现类,该元素是要放入到QueueDelayed队列中的。

 1 package com.zimo.mybaties.delayed;
 2 
 3 import org.springframework.transaction.annotation.Transactional;
 4 
 5 import java.util.concurrent.Delayed;
 6 import java.util.concurrent.TimeUnit;
 7 
 8 public class MyDelayedEvent implements Delayed{
 9     //要执行的任务
10     private Task task;
11 
12     private Long endTime;
13 
14     public MyDelayedEvent(Task task, Long endTime) {
15         this.task = task;
16         this.endTime = endTime;
17     }
18 
19     //获取剩余的时间,为0获取负数时取出
20     //TimeUnit.NANOSECONDS 毫微妙
21     @Override
22     public long getDelay(TimeUnit unit) {
23 //        return unit.convert(endTime,TimeUnit.MILLISECONDS) - unit.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS);
24         return unit.convert(endTime,TimeUnit.NANOSECONDS) - unit.convert(System.currentTimeMillis(),TimeUnit.NANOSECONDS);
25     }
26 
27     @Override
28     public int compareTo(Delayed o) {
29         if (this == o)
30             return 1;
31         if (o==null)
32             return -1;
33         long diff = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
34         return diff<0?-1:(diff==0?0:1);
35     }
36 
37     public Task getTask() {
38         return task;
39     }
40 
41     public void setTask(Task task) {
42         this.task = task;
43     }
44 
45     public Long getEndTime() {
46         return endTime;
47     }
48 
49     public void setEndTime(Long endTime) {
50         this.endTime = endTime;
51     }
52 }
MyDelayedEvent

其中Task是要执行的任务。创建一个Task接口,我们的任务都实现该接口,表示是一个任务调度到期后要执行的任务。

1 /**
2  * 任务
3  */
4 public interface Task {
5     //调用该方法,则会执行任务
6     void executeTask();
7 }
Task

下面是我们实现的其中一个任务

 1 package com.zimo.mybaties.delayed.delayedtest;
 2 
 3 import com.zimo.mybaties.delayed.Task;
 4 
 5 public class StudentTask implements Task{
 6 
 7     private Integer student;
 8 
 9     public StudentTask(Integer student) {
10         this.student = student;
11     }
12 
13     @Override
14     public void executeTask() {
15         System.out.println("学生任务执行"+student);
16     }
17 
18     public Integer getStudent() {
19         return student;
20     }
21 
22     public void setStudent(Integer student) {
23         this.student = student;
24     }
25 }
StudentTask

 

上面的类创建完毕后,我们需要创建一个守护线程来后台执行任务调度是否到到期的查询,时间到了后执行。getDelay()返回剩余的时间,

1 public interface MyDelayedService {
2     //插入任务调度
3     void put(MyDelayedEvent delayed);
4     //移除任务调度
5     boolean remove(MyDelayedEvent delayed);
6     //初始化该任务调度
7     void init();
8 }
MyDelayedService

 MyDelayedService的实现类

 

package com.zimo.mybaties.delayed;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Service
public class MyDelayedServiceImp implements MyDelayedService{

    private static final Logger logger = LoggerFactory.getLogger(MyDelayedServiceImp.class);
    private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private DelayQueue<MyDelayedEvent> queue = new DelayQueue<>();
    private Executor executor = Executors.newFixedThreadPool(30);//线程池,保证同一时刻执行的任务能执行s
    private Thread damon;//守护线程

    @Override
    public void init(){
        logger.info("初始化学生守护线程");
        damon = new Thread(() -> execute()); //新建一个线程,执行execute方法
        damon.setDaemon(true);  //设置为守护线程
        damon.setName("student queue thread");  //线程名称
        damon.start();  //启动线程
    }

    @Override
    public void put(MyDelayedEvent delayed){
        logger.info("插入任务");
        queue.put(delayed);
    }

    @Override
    public boolean remove(MyDelayedEvent delayed){
        logger.info("移除任务");
        return queue.remove(delayed);
    }

    private void execute(){
        while (true){
            //该线程要执行的内容
            try {
                MyDelayedEvent delayed = queue.take();
                if (delayed!=null){
                    logger.info("执行任务,任务执行时当前时间是 {}",TIME_FORMAT.format(delayed.getEndTime()));
                    executor.execute(new Runnable() {
                        //将执行的任务放入线程池,同一个时刻可能有多个任务要执行
                        @Override
                        public void run() {
                            delayed.getTask().executeTask();//执行任务
                        }
                    });

                }
            }catch (InterruptedException e){
                logger.error("任务调度被中断");
            }
        }
    }
}
MyDelayedServiceImp

 

通过测试可知,设置同一时刻的多个任务调度时,在时间到了之后,会全部进行执行。

 1 2018-08-28 18:04:19.020  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
 2 2018-08-28 18:04:19.020  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
 3 学生任务执行0
 4 学生任务执行8
 5 2018-08-28 18:04:19.021  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
 6 2018-08-28 18:04:19.021  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
 7 学生任务执行7
 8 2018-08-28 18:04:19.021  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
 9 学生任务执行6
10 学生任务执行5
11 2018-08-28 18:04:19.022  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
12 2018-08-28 18:04:19.022  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
13 学生任务执行4
14 2018-08-28 18:04:19.022  INFO 48417 --- [nt queue thread] c.z.mybaties.delayed.MyDelayedEventList  : 执行任务,任务执行时当前时间是 2018-08-28 18:04:19
15 学生任务执行2
16 学生任务执行1
Test Code

 当要执行的线程数大于线程池的最大数目时,会进行等待。

 

?如何移除我们要取消的任务呢。

首先我们的任务是存放如QueueDelayed队列中的,在本文中。他存放于类MyDelayedServiceImp中的 queue变量中。其内存放是实现了delayed接口的元素。通过查看DelayedQueue的文档可知。

其通过remove(Object o)来移除,o是我们的元素。通过查看remove的实现可知。其内是通过equals比较两个对象是否相等来判断是否是同一个delayed。所以我们需要在我们的QueueDelayed中实现我们的equals方法,使其两个对象之间的相等能够进行判断。

内部实现:

    /**
     * Removes a single instance of the specified element from this
     * queue, if it is present, whether or not it has expired.
     */
    public boolean remove(Object o) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return q.remove(o);//进入该方法
        } finally {
            lock.unlock();
        }
    }
public boolean remove(Object o) {
        int i = indexOf(o);
        if (i == -1)
            return false;
        else {
            removeAt(i);
            return true;
        }
    }

private int indexOf(Object o) {
        if (o != null) {
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))  //比较
                    return i;
        }
        return -1;
    }

 

我们的实现;实现我们的Delayed元素的equals 和hashCode。

package com.zimo.mybaties.delayed;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class MyDelayedEvent implements Delayed{
    //要执行的任务
    private Task task;
    private String uniqueKey; //该uniqueKey的生成规则根据业务来进行确定。只需要确保uniqueKey是唯一标识的
    private Long endTime;

    public MyDelayedEvent(Task task, Long endTime) {
        this.task = task;
        this.endTime = endTime;
    }

    public MyDelayedEvent(Task task, Long endTime, Integer targetClassId) {
        this.task = task;
        this.endTime = endTime;
        setUniqueKey(targetClassId);
        System.out.println("unique key : "+getUniqueKey());
    }

    //获取剩余的时间,为0获取负数时取出
    //TimeUnit.NANOSECONDS 毫微妙
    @Override
    public long getDelay(TimeUnit unit) {
//        return unit.convert(endTime,TimeUnit.MILLISECONDS) - unit.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS);
        return unit.convert(endTime,TimeUnit.NANOSECONDS) - unit.convert(System.currentTimeMillis(),TimeUnit.NANOSECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        if (this == o)
            return 1;
        if (o==null)
            return -1;
        long diff = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return diff<0?-1:(diff==0?0:1);
    }

    @Override
    public int hashCode() {
        final int prime = 31; //hashCode就是用的31
        int result = 1;
        result = prime*result + endTime.hashCode();
        result = prime*result + ((uniqueKey==null)?0:uniqueKey.hashCode());
        //我这里因为Task也是一个对象,为了简便,所以不用task作为hashCode生成对象。而是新增加一个uniqueKey。
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj)
            return true;
        if (obj == null)
            return false;
        if (getClass()!=obj.getClass())
            return false;
        MyDelayedEvent o = (MyDelayedEvent)obj;
        //Long对象的比较,判断值是否相同也是通过equals
        if (!getEndTime().equals(o.getEndTime()))
            return false;
        if (!getUniqueKey().equals(o.getUniqueKey()))
            return false;
        return true;
    }

    public String getUniqueKey() {
        return uniqueKey;
    }

    public void setUniqueKey(Integer targetClassId) {
        //uniqueKey生成规则
        this.uniqueKey = new StringBuffer().append(task.getClass()).append(targetClassId).append(endTime).toString();
    }

    public Task getTask() {
        return task;
    }

    public void setTask(Task task) {
        this.task = task;
    }

    public Long getEndTime() {
        return endTime;
    }

    public void setEndTime(Long endTime) {
        this.endTime = endTime;
    }
}

 后面有需要新的定时任务需求的时候,只需要新增一个实现了Task接口的实现类即可。

public class AssistantTask implements Task {

    @Override
    public void executeTask() {
        System.out.println("assistant task run ");
    }
}
MyDelayedEvent delayed_assistant = new MyDelayedEvent(new AssistantTask(),nowTime+delay*1000+1000,2);
            myDelayedService.put(delayed_assistant);

 

转载于:https://www.cnblogs.com/wbq1113/p/9549780.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值