zoukankan      html  css  js  c++  java
  • 几种定时任务(Timer、TimerTask、ScheduledFuture)的退出—结合真实案例【JAVA并发】

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

    需求说明:定时更新正在生成的文件大小和状态【进行中、失败、完成】,如果文件生成完成,则退出【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年内把代码写好,技术博客字字推敲,坚持零拷贝和原创
    • 写博客的意义在于锻炼逻辑条理性,加深对知识的系统性理解,锻炼文笔,如果恰好又对别人有点帮助,那真是一件令人开心的事

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

  • 相关阅读:
    centos7 设置mongodb远程连接
    计算两个坐标点之间的点的坐标
    vim 设置字体和解决乱码
    webpack无法通过 IP 地址访问 localhost 解决方案
    使用GitHub作为Maven仓库并引用
    ajax 文件下载
    展开被 SpringBoot 玩的日子 《 六 》 整合 Mybatis
    展开被 SpringBoot 玩的日子 《 五 》 spring data jpa 的使用
    展开被 SpringBoot 玩的日子 《 四 》 Session 会话共享
    展开被 SpringBoot 玩的日子 《 三 》 整合Redis
  • 原文地址:https://www.cnblogs.com/NaughtyCat/p/exit-way-of-several-schedule-task-in-java.html
Copyright © 2011-2022 走看看