几种定时任务(Timer、TimerTask、ScheduledFuture)的退出—结合真实案例【JAVA并发】

摘要:
while(true){try{Thread.sleep(updateInternal*1000);if(isFailed(status)||hasNoData(status)|| isFinished(status)){break;

几种定时任务(Timer、TimerTask、ScheduledFuture)的退出—结合真实案例【JAVA并发】第1张

工作中常常会有定时任务的开发需求,特别是移动端。最近笔者正好有所涉及,鉴于此,结合开发中的案例说明一下几种定时任务的退出

需求说明:定时更新正在生成的文件大小和状态【进行中、失败、完成】,如果文件生成完成,则退出【CoderBaby

调度可以用Timer 【调用schedule()或者scheduleAtFixedRate()方法实现】或者ScheduledExecutorService   【结合工作中其它的需求,笔者选用此】

ScheduledExecutorService的初始化(线程池):

private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
  • 手动实现——最朴素的方案【通过sleep来控制时间间隔、break来退出】
            scheduledExecutorService.execute(() -> {
                long oldCurFileSize = 0;
                while(true) {
                    try {
                        Thread.sleep(updateInternal * 1000);
                        long curFileSize = Files.size(Paths.get(TMP_PCAP_PATH + tmpPcapFileName));
                        int status = getStatus(tmpPcapFileName);
                        if (isFailed(status) || hasNoData(status) || isFinished(status)) {
                            break;
                        }
                        if (curFileSize != oldCurFileSize) {
                            updateFileInfo(tmpPcapFileName, curFileSize, 0);
                            oldCurFileSize = curFileSize;
                        } else {
                            updateFileInfo(tmpPcapFileName, curFileSize, 3);
                            // 延迟1秒,才能成功更新
                            Thread.sleep(1000);
                            break;
                        }
                    } catch (Exception e) {
                        logger.warn("Catch exception : " + e.toString());
                    }
                }
            });

注:

updateFileInfo—更新数据库相关记录;

getStatus查询数据库当前记录的状态,判定是否完成或者出现错误;

updateInternal控制定时任务的运行时间间隔(单位为秒)

  • TimerTask【通过cancel来退出】

 定义一个内部类继承自TimerTask抽象类

    class ScheduledUpdateTrafficForensics extends TimerTask {
        private String tmpPcapFileName;
        private long oldCurrentFileSize = 0;

        public ScheduledUpdateTrafficForensics(String tmpPcapFileName) {
            this.tmpPcapFileName = tmpPcapFileName;
        }

        public void run() {
            try {
                long currentFileSize = Files.size(Paths.get(TMP_PCAP_PATH + tmpPcapFileName));
                int status = getStatus(tmpPcapFileName);
                if (isFailed(status) || hasNoData(status) || isFinished(status)) {
                    this.cancel();
                }
                if (oldCurrentFileSize != currentFileSize) {
                    updateFileInfo(tmpPcapFileName, currentFileSize, 0);
            oldCurrentFileSize = currentFileSize;   }
else { updateFileInfo(tmpPcapFileName, currentFileSize, 3); this.cancel(); } } catch (IOException e) { logger.warn("Catch exception : " + e.toString()); } } }

 通过scheduleAtFixedRate接口来调用(设置时间间隔和且第一次执行的延迟时间)

            scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new ScheduledUpdateTrafficForensics(tmpPcapFileName),
                    updateInternal, pcapDownloadStatusUpdateInternal, TimeUnit.SECONDS);
  • ScheduledFuture【通过cancle和读取isCancelled结果来退出】

定义一个内部类实现Runnable接口

     class ScheduledUpdateTrafficForensics implements Runnable {
        private String tmpPcapFileName;
        private long oldCurrentFileSize = 0;

        public ScheduledUpdateTrafficForensics(String tmpPcapFileName) {
            this.tmpPcapFileName = tmpPcapFileName;
        }

        public void run() {
            while (!scheduledFuture.isCancelled()) {
                try {
                    long currentFileSize = Files.size(Paths.get(TMP_PCAP_PATH + tmpPcapFileName));
                    int status = getStatus(tmpPcapFileName);
                    if (isFailed(status) || hasNoData(status) || isFinished(status) {
                        scheduledFuture.cancel(true);
                        return;
                    }
                    if (oldCurrentFileSize != currentFileSzie) {
                        updateFileInfo(tmpPcapFileName, currentFileSize, 0);
               oldCurrentFileSize = currentFileSize; } else {
               updateFileInfo(tmpPacpFileName, currentFileSize, 3);
               scheduleFuture.canle(true);
            } }
catch (IOException e) { logger.warn("Catch exception : " + e.toString()); } } } }

 通过scheduleAtFixedRate接口来调用(设置时间间隔和且第一次执行的延迟时间),并且将结果返回给ScheduledFuture

            scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new ScheduledUpdateTrafficForensics(tmpPcapFileName),
                    updateInternal, pcapDownloadStatusUpdateInternal, TimeUnit.SECONDS);

注:

通过scheduledFuture.cancel(true)后可能不能成功结束定时任务,所以必须通过手动调用isCancelled()来判断是否被cancle(调用cancel后,再调用isCancelled() 【一定会返回true】)掉了,然后退出任务。相关源码注释如下:

     * <p>After this method returns, subsequent calls to {@link #isDone} will
     * always return {@code true}.  Subsequent calls to {@link #isCancelled}
     * will always return {@code true} if this method returned {@code true}.

特别说明:

关于schedule(时间基准:运行的实际时间)和scheduleAtFixedRate(时间基准:理论时间点)的区别:

  • scheduleAtFixedRate调度一个task,在delay(ms)后开始调度,然后每经过period(ms)再次调度,貌似和方法—schedule是一样的,其实不然。
  • schedule在计算下一次执行的时间的时候,是通过当前时间(在任务执行前得到) + 时间片,而scheduleAtFixedRate方法是通过当前需要执行的时间(也就是计算出现在应该执行的时间)+ 时间片,前者是运行的实际时间,而后者是理论时间点。

例如:schedule时间片是5s,那么理论上会在5、10、15、20这些时间片被调度,但是如果由于某些CPU征用导致未被调度,假如等到第8s才被第一次调度,那么schedule方法计算出来的下一次时间应该是第13s而不是第10s,这样有可能下次就越到20s后而被少调度一次或多次,而scheduleAtFixedRate方法就是每次理论计算出下一次需要调度的时间用以排序,若第8s被调度,那么计算出应该是第10s,所以它距离当前时间是2s,那么再调度队列排序中,会被优先调度,那么就尽量减少漏掉调度的情况。

详情请移步https://www.cnblogs.com/dolphin0520/p/3938991.html

*********************************************************************************

精力有限,想法太多,专注做好一件事就行

  • 我只是一个程序猿。5年内把代码写好,技术博客字字推敲,坚持零拷贝和原创
  • 写博客的意义在于锻炼逻辑条理性,加深对知识的系统性理解,锻炼文笔,如果恰好又对别人有点帮助,那真是一件令人开心的事

*********************************************************************************

免责声明:文章转载自《几种定时任务(Timer、TimerTask、ScheduledFuture)的退出—结合真实案例【JAVA并发】》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇几种开源分词工具的比較Bluedroid: 音频数据的传输流程下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Quartz 设置一个半小时任务实现

该文章属于本人原创,转载请注明出处。   spring + Quartz 设置定时任务时要求没一个半小时执行一次   设置两个相同的定时任务   第一个从整点开始每三小时执行一次              <!--每三小时执行一次任务,从整点开始-->        <propertyname="cronExpression"valu...

转 将python的datetime转换为unix时间戳

python datetime unix时间戳以及字符串时间戳转换   将python的datetime转换为unix时间戳 import time import datetime dtime = datetime.datetime.now() ans_time = time.mktime(dtime.timetuple())  将unix时间戳转换为...

【iOS】Instruments性能检测之耗电优化

  耗电优化最终目的:通过尽可能降低CPU、GPU功耗来降低手机电量消耗。   (1)尽可能少用定时器;   (2)优化I/O操作(所谓的I/O操作也就是文件操作,我们简称为I/O操作。怎么优化呢?尽量不要频繁写入小数据,最好批量一次性写入。读写大量主要的数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispat...

Linux下计划任务以及crontab权限问题

在Linux工作环境下,我们有时可能会需要在未来某个时间执行某个命令或脚本,但是我们又不可能定个闹钟,然后到点了再去执行吧,这多麻烦。还好我们的Linux系统这么强大,提供了任务计划这个功能,我们就不需要守着点去执行相应的命令或脚本了。当我们定义好了任务计划之后,就可以去做别的事情了,等到了我们自己定义的那个时间点,你所定义的任务操作系统会自动执行,这就被...

gulp前端自动化环境搭建详解

  1、安装 nodejs    Grunt和所有grunt插件都是基于nodejs来运行的, https://nodejs.org/    安装完成之后在终端 node -v 查看安装版本  npm -v 查看npm版本        选装cnpm 1.1、说明:因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常, 如果npm的服务器在中...

java 字符串如何直接转LocalDateTime?

  1.情景展示   在实际开发过程中,可能会遇到将前端传的日期格式转成LocalDateTime插入到数据库的情况,如何将日期转成LocalDateTime呢? 2.原因分析   在Java8中,日期类不同于以前版本的java.util.Date工具类,Date类可以存日期也可以存时间,还能存日期+时间,统统都能塞进去;   但java8中将日期与时间...