欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用DelayQueue构建自己的ThreadPoolExecutor

程序员文章站 2022-04-19 11:02:48
...

前言

 

在日常java开发过程中使用线程池一般都是通过Executors提供的静态方法创建线程池,但目前还没有提供使用DelayQueue(延迟队列)作为任务队列的线程池创建方法。在笔者另一篇博客中《DelayQueue--阅读源码从jdk开始》,有个场景需要使用DelayQueue实现定时的页面发布功能,在那次实现过程中使用DelayQueuetake方法获取到任务后再放入线程池,由于这里是串行take,如果在同一时刻有多个任务需要被执行,这时势必有有延迟,虽然延迟不多,但不是最佳实现方案。

 

通过前一篇对ThreadPoolExecutor总结(这里),我们可以直接使用ThreadPoolExecutor的构造方法构造自定义的线程池,使用DelayQueue作为任务队列即可。

 

使用DelayQueue创建线程池

 

这个步骤很简单,只要理解了ThreadPoolExecutor构造方法的各个参数即可(对各个参数的详细讲解见上一篇文章)

DelayQueue queue = new DelayQueue<>();//延迟队列
       
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,10,1000l, TimeUnit.MILLISECONDS,queue);

之后,只需调用ThreadPoolExecutorexecute提交任务即可。

 

创建延迟任务类

 

我们知道ThreadPoolExecutorexecute方法,需要一个实现了Runnable接口的对象,那么这个任务类必须是实现Runnable接口;并且最终这个对象要能放到DelayQueue中,这个任务类必须实现Delayed接口。最终这个任务类实现如下:

public class TaskInfo implements Delayed,Runnable {
 
    //任务id
    private int id;
 
    //业务类型
    private int type;
 
    //业务数据
    private String data;
 
    //执行时间
    private long excuteTime;
 
    public TaskInfo(int id, int type, String data, long excuteTime) {
        this.id = id;
        this.type = type;
        this.data = data;
        this.excuteTime = TimeUnit.NANOSECONDS.convert(excuteTime, TimeUnit.MILLISECONDS)+System.nanoTime();
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public int getType() {
        return type;
    }
 
    public void setType(int type) {
        this.type = type;
    }
 
    public String getData() {
        return data;
    }
 
    public void setData(String data) {
        this.data = data;
    }
 
    public long getExcuteTime() {
        return excuteTime;
    }
 
    public void setExcuteTime(long excuteTime) {
        this.excuteTime = excuteTime;
    }
 
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.excuteTime- System.nanoTime() , TimeUnit.NANOSECONDS);
    }
 
    @Override
    public int compareTo(Delayed o) {
        TaskInfo msg = (TaskInfo)o;
        return this.excuteTime>msg.excuteTime?1:( this.excuteTime<msg.excuteTime?-1:0);
    }
 
    @Override
    public void run() {
        System.out.println("run task:"+id);
    }
}

初始化核心线程

 

上面已经创建好任务类了,也许大家会觉得直接new TaskInfo(),并且调用ThreadPoolExecutorexecute方法提交任务就行,如下:

 
//创建任务
TaskInfo t1 = new TaskInfo(1,1,"任务1",8000);
TaskInfo t2 = new TaskInfo(2,2,"任务2",8000);
 
//提交任务
threadPoolExecutor.execute(t1);
threadPoolExecutor.execute(t2);
 

 

通过前一篇文章的分析,在线程池刚初始化时,由于核心线程数为0,此时执行execute提交任务,任务不会进入延迟队列,而是直接执行,就无法满足业务需求(任务被提前执行了)。正确做法是在线程初始化完成后,先调用prestartAllCoreThreads方法,先创建好核心线程,即:

threadPoolExecutor.prestartAllCoreThreads();

 

 

完成示例代码:

public class ThreadPoolExecutorTest {
 
    private static ExecutorService es =  Executors.newFixedThreadPool(3);//3个线程的线程池
 
    public static void main(String[] args){
        DelayQueue queue = new DelayQueue<>();//延迟队列
 
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,10,1000l, TimeUnit.MILLISECONDS,queue);
        threadPoolExecutor.prestartAllCoreThreads();//初始化核心线程
 
        TaskInfo t1 = new TaskInfo(1,1,"任务1",8000);
        TaskInfo t2 = new TaskInfo(2,2,"任务2",8000);
        TaskInfo t3 = new TaskInfo(3,3,"任务3",9000);
        TaskInfo t4 = new TaskInfo(4,4,"任务4",5000);
        TaskInfo t5 = new TaskInfo(5,5,"任务5",5000);
        TaskInfo t6 = new TaskInfo(6,6,"任务6",6000);
        TaskInfo t7 = new TaskInfo(7,7,"任务7",7000);
        TaskInfo t8 = new TaskInfo(8,8,"任务8",10000);
        threadPoolExecutor.execute(t1);
        threadPoolExecutor.execute(t2);
        threadPoolExecutor.execute(t3);
        threadPoolExecutor.execute(t4);
        threadPoolExecutor.execute(t5);
        threadPoolExecutor.execute(t6);
        threadPoolExecutor.execute(t7);
        threadPoolExecutor.execute(t8);
 
    }
}

 

 

执行main方法,可以发现任务是按时延迟执行的,而且如果在同一刻如果有多个任务需要执行,这时也可以利用线程池并行执行,进一步降低延迟。

 

另外大家也可以注释掉threadPoolExecutor.prestartAllCoreThreads();这句,验证下如果不初始化核心线程会有什么后果。

 

 

心灵鸡汤

有的程序员觉得整天实现一些简单的功能没有技术含量,如果你觉得某项工作没有技术含量,那只是你自己把它做得没有技术含量,认真的写好自己的每一行代码,不停的去完善,它就会成为有技术含量的工作。想想达芬奇画鸡蛋的故事。

 

 

                                                                                     摘自--《天星老师语录》