博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程(4):使用线程池执行定时任务
阅读量:7066 次
发布时间:2019-06-28

本文共 3415 字,大约阅读时间需要 11 分钟。

在现实世界里,我们总是免不了要定期去做一件事情(比如上课)—— 在计算机的世界里,更是如此。比如我们手机每天叫我们起床的电子闹钟,某些网站会定期向我们发送一些推荐相关的邮件,集群中我们需要每隔一定时间检查是否有机器宕机等。


在 中已经介绍,JDK 1.5 时,标准类库添加了对线程池的支持,然后在线程池核心实现 ThreadPoolExecutor 的基础上,实现了 ScheduledThreadPoolExecutor,作为可以 定时和周期性执行任务 的线程池。ScheduledThreadPoolExecutor 的类图如下:

ScheduledThreadPoolExecutor 的类图

ScheduledThreadPoolExecutor 实现了 ScheduledExecutorService 接口,ScheduledExecutorService 继承了 ExecutorService 接口,所以首先 ScheduledThreadPoolExecutor 是一个 ExecutorService (线程池),然后除了具有线程池的功能,它还有定时和周期性执行任务的功能。ScheduledExecutorService 除了从 ExecutorService 继承的方法外,还包括如下四个方法:

ScheduledExecutorService 定义的四个方法

第一个 Schedule 方法:

Schedule 方法

delay 指定的时间后,执行指定的 Runnable 任务,可以通过返回的 ScheduledFuture<?> 与该任务进行交互。

第二个 Schedule 方法:

第二个 Schedule 方法

delay 指定的时间后,执行指定的 Callable<V> 任务,可以通过返回的 ScheduledFuture<V> 与该任务进行交互。

ScheduledFuture 接口 继承自 Future 接口,所以 ScheduledFuture 和任务的交互方式与 Future 一致。所以通过ScheduledFuture,可以 判断定时任务是否已经完成,获得定时任务的返回值,或者取消任务等)

scheduleAtFixedRate 方法:

scheduleAtFixedRate 方法

initialDelay 指定的时间后,开始按周期 period 执行指定的 Runnable 任务。

假设调用该方法后的时间点为 0,那么第一次执行任务的时间点为 initialDelay,第二次为 initialDelay + period,第三次为 initialDelay + period + period,以此类推。

scheduleWithFixedDelay 方法:

scheduleWithFixedDelay 方法

initialDelay 指定的时间后,开始按指定的 delay 延期性的执行指定的 Runnable 任务。

假设调用该方法后的时间点为 0,每次任务需要耗时 T(i)i 为第几次执行任务),那么第一次执行任务的时间点为 initialDelay,第一次完成任务的时间点为 initialDelay + T(1),则第二次执行任务的时间点为 initialDelay + T(1) + delay;第二次完成任务的时间点为 initialDelay + (T(1) + delay) + T(2),所以第三次执行任务的时间点为 initialDelay + T(1) + delay + T(2) + delay,以此类推。


我们来实践下 ScheduledThreadPoolExecutorscheduleAtFixedRate 方法:

public class ScheduledExecutorServiceTest {    public static void main(String[] args) throws Exception {        ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();                TimerTask timerTask = new TimerTask(2000); // 任务需要 2000 ms 才能执行完毕        System.out.printf("起始时间:%s\n\n", new SimpleDateFormat("HH:mm:ss").format(new Date()));        // 延时 1 秒后,按 3 秒的周期执行任务        timer.scheduleAtFixedRate(timerTask, 1000, 3000, TimeUnit.MILLISECONDS);    }    private static class TimerTask implements Runnable {        private final int sleepTime;        private final SimpleDateFormat dateFormat;        public TimerTask(int sleepTime) {            this.sleepTime = sleepTime;            dateFormat = new SimpleDateFormat("HH:mm:ss");        }        @Override        public void run() {            System.out.println("任务开始,当前时间:" + dateFormat.format(new Date()));            try {                System.out.println("模拟任务运行...");                Thread.sleep(sleepTime);            } catch (InterruptedException ex) {                ex.printStackTrace(System.err);            }            System.out.println("任务结束,当前时间:" + dateFormat.format(new Date()));            System.out.println();        }    }}

运行结果:

运行结果

可以看到运行结果完全符合预期 —— 延时 1 秒后,每隔 3 秒执行一次任务。


上面是任务的运行时间小于周期时间的情况 —— 那如果任务运行的时间大于给定的执行周期呢?(比如任务运行需要 3 s,但是我们指定的周期为 2 s)

修改 main 方法:

public static void main(String[] args) throws Exception {    ScheduledExecutorService timer = Executors.newScheduledThreadPool(2);    TimerTask timerTask = new TimerTask(3000); // 每个任务需要 3000 ms 才能执行完毕    System.out.printf("起始时间:%s\n\n", new SimpleDateFormat("HH:mm:ss").format(new Date()));    timer.scheduleAtFixedRate(timerTask, 1000, 2000, TimeUnit.MILLISECONDS);}

运行结果:

运行结果

可以看到此时虽然我们指定的周期为 2 s,但是因为任务的运行就需要 3 s(超过周期),所以这种情况下 scheduleAtFixedRate 的处理方式为 上一次任务刚完成,则紧接着立即运行下一次任务,而不是使用线程池中的空闲线程来运行任务以维护 2 秒这个周期 —— 由此可见,每个定时任务在 ScheduledThreadPoolExecutor 中,都是串行运行的,即下一次运行任务一定在上一次任务结束之后。

scheduleWithFixedDelay 方法 的使用也十分简单,请有兴趣的读者自己实践)

转载地址:http://lkall.baihongyu.com/

你可能感兴趣的文章
微信,QQ这类IM app怎么做——谈谈Websocket
查看>>
在Ubuntu 11.04中安装Openresty
查看>>
JAVA常见的面试题
查看>>
《Python高效开发实战》实战演练——建立应用2
查看>>
java: -source 1.6 中不支持 switch 中存在字符串.....
查看>>
Confluence 6 空间
查看>>
lua-resty-http上传数据
查看>>
heartbeat+ldirectord实现web与dns的高可用性
查看>>
luacurl安装
查看>>
JBoss的配置
查看>>
软件测试之Web实战测试
查看>>
Tomcat的参数配置及一般问题的解决
查看>>
JAVA CAS原理深度分析
查看>>
O2O?啥是“呕吐呕”?
查看>>
百度的疯狂 UC的隐忍
查看>>
我的友情链接
查看>>
AGG第三十五课 gsv_text 渲染ASCII字符
查看>>
查找组成一个偶数最接近的两个素数
查看>>
不怕狼一样的敌人,就怕狗一样的朋友
查看>>
bash基础特性
查看>>