Build your own ThreadPoolExecutor with DelayQueue

foreword

 

In the daily java development process, thread pools are generally created by static methods provided by Executors , but there is no thread pool creation method that uses DelayQueue (delay queue) as a task queue. In another blog of the author, " DelayQueue-- Reading the source code starts with jdk " , there is a scene that needs to use DelayQueue to realize the timed page publishing function. During the implementation process, the take method of DelayQueue is used to obtain the task and then put it into the thread For the pool, since this is a serial take , if there are multiple tasks that need to be executed at the same time, there is bound to be a delay. Although the delay is not much, it is not the best solution.

 

Through the previous summary of ThreadPoolExecutor ( click here ) , we can directly use the construction method of ThreadPoolExecutor to construct a custom thread pool, and use DelayQueue as the " task queue " .

 

Create a thread pool with DelayQueue

 

This step is very simple, as long as you understand the parameters of the ThreadPoolExecutor construction method ( see the previous article for a detailed explanation of each parameter ) :

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

After that, just call the execute of ThreadPoolExecutor to submit the task.

 

Create a delayed task class

 

We know that the execute method of ThreadPoolExecutor needs an object that implements the Runnable interface, then the task class must implement the Runnable interface; and finally, if the object can be placed in the DelayQueue , the task class must implement the Delayed interface. The final implementation of this task class is as follows:

public class TaskInfo implements Delayed,Runnable {
 
    //task id
    private int id;
 
    //business type
    private int type;
 
    // business data
    private String data;
 
    //execution time
    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();这句,验证下如果不初始化核心线程会有什么后果。

 

 

心灵鸡汤

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

 

 

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

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326404329&siteId=291194637