利用ScheduledThreadPoolExecutor定时执行任务

2023-11-10

         最近时间好忙,终于抽出时间来写一篇博客了,想了想,把之前遇到的一个小bug分享一下吧,之前在做时钟插件时候,我用到了一个定时器,即大家常用的TimerTask,但它总是意外的停止, 就是我开启了一个定时器  每一秒都会走run方法,  有时候定时器就突然停止了 ,它自己的cancel()方法它也没走。直到后来我发现需要创建一些个延迟的、并具有周期性的任务,有两种方法可以去去创建延迟周期性任务。

   第一种

 Timer
 Timer是java.util包下的一个类,在JDK1.3的时候被引入,Timer只是充当了一个执行者的角色,真正的任务逻辑是通过一个叫做TimerTask的抽象类完成的,TimerTask也是  java.util包下面的类,它是一个实现了Runnable接口的抽象类,包含一个抽象方法run( )方法,需要我们自己去提供具体的业务实现。
 Timer类对象是通过其schedule方法执行TimerTask对象中定义的业务逻辑,并且schedule方法拥有多个重载方法提供不同的延迟与周期性服务。
下面是利用Timer去创建的一个延时周期性任务。

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;


public class TestTimer {


  public static void main(String[] args) {
    
    String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
    System.out.println("Start time : " + time);
    
    Timer timer = new Timer();
    TimerTask task = new TimerTask() {
      
      @Override
      public void run() {
        // TODO Auto-generated method stub
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
        System.out.println("Now Time : "  + time);
      }
    }; //end task
    
    timer.schedule(task, 2000, 3000);
    
  }
  
}


程序的输出:
Start time : 21:36:08
Now Time : 21:36:10
Now Time : 21:36:13
Now Time : 21:36:16
Now Time : 21:36:19

第二种:

ScheduledThreadPoolExecutor
在JDK1.5的时候在java.util.concurrent并发包下引入了ScheduledThreadPoolExecutor类,引入它的原因是因为Timer类创建的延迟周期性任务存在一些缺陷, ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,并且实现了ScheduledExecutorService接口, ScheduledThreadPoolExecutor也是通过schedule方法执行Runnable任务的。
我们用 ScheduledThreadPoolExecutor来实现和上述Timer一样的功能。

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class TestScheduledThreadPoolExecutor {


  public static void main(String[] args) {
    
    String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
    System.out.println("Start time : " + time);
    
    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);  //创建5个执行线程
    
    Runnable runnable = new Runnable() {
      
      @Override
      public void run() {
        // TODO Auto-generated method stub
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
        System.out.println("Now Time : "  + time);
      }
    };
    
    executor.scheduleWithFixedDelay(runnable, 2, 3, TimeUnit.SECONDS);
    
  }
  
}


程序的输出:
Start time : 22:12:25
Now Time : 22:12:27
Now Time : 22:12:30
Now Time : 22:12:33
Now Time : 22:12:36

这样看来Timer和 ScheduledThreadPoolExecutor好像没有声明差别,但是 ScheduledThreadPoolExecutor的引入正是由于Timer类存在的一些不足,并且在JDK1.5或更高版本中,几乎没有利用继续使用Timer类,下面说明Timer存在的一些缺点。
单线程
Timer类是通过单线程来执行所有的TimerTask任务的,如果一个任务的执行过程非常耗时,将会导致其他任务的时效性出现问题。而 ScheduledThreadPoolExecutor是基于线程池的多线程执行任务,不会存在这样的问题。
这里我们通过让Timer来执行两个TimerTask任务来说明,其中一个TimerTask的执行过程是耗时的,加入需要2秒。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;


public class SingleThreadTimer {


  public static void main(String[] args) {
    
    String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
    System.out.println("Start time : " + time);
    
    Timer timer = new Timer();
    
    TimerTask task1 = new TimerTask() {
      
      @Override
      public void run() {
        // TODO Auto-generated method stub
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
        System.out.println("Task1 time : " + time);
      }
    };
    
    TimerTask task2 = new TimerTask() {
      
      @Override
      public void run() {
        // TODO Auto-generated method stub
        try {
          Thread.sleep(2000);
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
        System.out.println("task2 time : " + time);
      }
    };
    
    timer.schedule(task1, 2000, 1000);
    timer.schedule(task2, 2000, 3000);
  }
  
}


这里定义了两个任务,任务1,程序启动2秒后每隔1秒运行一次,任务2,程序启动2秒后,每隔3秒运行1次,然后让Timer同时运行这两个任务
程序的输出如下:
Start time : 22:22:37
Task1 time : 22:22:39
task2 time : 22:22:41
Task1 time : 22:22:41
Task1 time : 22:22:42
task2 time : 22:22:44
Task1 time : 22:22:44
Task1 time : 22:22:45
task2 time : 22:22:47
Task1 time : 22:22:47
Task1 time : 22:22:48


可以分析,无论是任务1还是任务2都没有按照我们设定的预期进行运行,造成这个现象的原因就是Timer类是单线程的。
Timer线程不捕获异常
Timer类中是不捕获异常的,假如一个TimerTask中抛出未检查异常(P.S: java中异常分为两类:checked exception(检查异常)和unchecked exception(未检查异常),对于未检查异常也叫RuntimeException(运行时异常).  ),Timer类将不会处理这个异常而产生无法预料的错误。这样一个任务抛出异常将会导致整个Timer中的任务都被取消,此时已安排但未执行的TimerTask也永远不会执行了,新的任务也不能被调度(所谓的“线程泄漏”现象)。
下面就已常见的RuntimeException,ArrayIndexOutOfBoundsException数组越界异常,来演示这个缺点:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;


public class TestTimerTask {


  public static void main(String[] args) {
    System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
    Timer timer = new Timer();
    TimerTask task1 = new TimerTask() {
    
      @Override
      public void run() {
        System.out.println("1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
      }
    };
    
    TimerTask task2 = new TimerTask() {
      
      @Override
      public void run() {
        int[] arr = {1,2,3,4,5};
        try {
          Thread.sleep(1000);
          
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        int index = (int)(Math.random()*100);
        System.out.println(arr[index]);
        
        System.out.println("2: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
      }
    };
    
    timer.schedule(task1, 2000, 3000);
    timer.schedule(task2, 2000, 1000);
    
  }
  
}


程序会在运行过程中抛出数组越界异常,并且整个程序都会被终止,原来完好的任务1也被终止了。
基于绝对时间
Timer类的调度是基于绝对的时间的,而不是相对的时间,因此Timer类对系统时钟的变化是敏感的,举个例子,加入你希望任务1每个10秒执行一次,某个时刻,你将系统时间提前了6秒,那么任务1就会在4秒后执行,而不是10秒后。在 ScheduledThreadPoolExecutor,任务的调度是基于相对时间的,原因是它在任务的内部 存储了该任务距离下次调度还需要的时间(使用的是基于 System#nanoTime实现的相对时间 ,不会因为系统时间改变而改变,如距离下次执行还有10秒,不会因为将系统时间调前6秒而变成4秒后执行)。
基于以上3个弊端,在JDK1.5或以上版本中,我们几乎没有理由继续使用Timer类,ScheduledThreadPoolExecutor可以很好的去替代Timer类来完成延迟周期性任务。

所以在以后的定时器使用上面,强烈建议使用ScheduledThreadPoolExecutor。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用ScheduledThreadPoolExecutor定时执行任务 的相关文章

  • dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper 无法转换为 android.app.Activity

    我正在使用具有一些自定义视图实现的第三方库 在实现中 他们调用Utils showSoftKeyboard context as Activity 这与使用片段作为 AndroidEntryPoint 一起导致以下异常 java lang
  • 在 Android 中从麦克风录制 wav 文件 - 问题

    我需要能够使用 Android 中的麦克风创建 WAV 文件 目前 我遇到了很多麻烦 到目前为止 这就是我的情况 我正在使用 micDroid 项目代码的一部分来记录 read thread int sampleRate 44100 int
  • Android 从 C++ 端播放原始音频

    我需要能够在 Android 系统的 C 端以自定义文件格式传输音频 我正在致力于移植自定义媒体播放器 并且需要能够打开自定义文件并从中传输音频 这很重要 因为我认为从性能角度来看将整个播放器移植到 JAVA 是不可行的 并且通过 JNI
  • 多种语言的多种字体

    我最近在开发应用程序时遇到了一种情况 我必须在文本视图中显示不同的语言 目前我正在展示一些使用字体 字体像这样 Typeface tf Typeface createFromAsset this getAssets DroidHindi t
  • 如何实现 ALTER TABLE 的示例[重复]

    这个问题在这里已经有答案了 我已经多次问过这个问题 但尚未得到完整的答案 如何实现 ALTER TABLE 语句以向数据库添加列 有人可以给我举个例子吗 请阅读SQLite ALTER TABLE 参考 http sqlite org la
  • SQLite FTS4 使用特殊字符进行搜索

    我有一个 Android 应用程序 它使用 FTS4 虚拟表在 SQLite 数据库中搜索数据 它工作正常 但是当表中的数据包含特殊字符 如 或 时 SQLite MATCH 函数不会给出任何结果 我现在迷路了 谢谢 注意 默认的分词器真的
  • 更新到 Kotlin 1.3.30 后出现“未解析的引用:Parcelize”

    我使用 Kotlin 1 3 21 很长时间了kotlin android extensions插件长期处于实验模式 今天我通过升级版本切换到 Kotlin 1 3 30 现在无论我使用什么 Parcelize注释我看到错误 Unresol
  • RecyclerView 在聊天屏幕中的 notificationDataSetChanged 上滚动到顶部

    我正在尝试使用 recyclerView 创建消息传递类型的屏幕 该屏幕将从底部开始 并在用户到达聊天顶端时加载更多数据 但我面临着这个奇怪的问题 我的 recyclerView 在调用 notificationDataSetChanged
  • 在 Android 中跨单元测试和仪器测试共享资源

    现在谷歌已经添加了实验单元测试支持 http tools android com tech docs unit testing support 如何在单元测试和仪器测试之间共享资源 例如 假设我有一个TestUtils java我希望在单元
  • 表面视图+gl表面视图+框架布局

    我是 java 和 OpenGL 的新手 我正在尝试获得一个相机预览屏幕 能够 同时显示 3D 对象 浏览完样本后 api 演示 我想结合示例的代码 api 演示就足够了 但不知何故它不起作用 迫使我 启动时关闭 错误被称为空指针 例外 有
  • 通知声音不适用于 api 10 android

    我用这个功能来显示状态notification 一切正常 但没有声音播放notification public void notifiction main String ticker String title String text int
  • 如何仅从 Firestore 获取最新更新的数据?

    在 Firestore 上发现任何更改时始终获取整个文档 如何只获取最近更新的数据 这是我的数据 我需要在第一次加载时在聊天中按对象顺序 例如 2018 09 17 30 40 msg和sendby 并且如果数据更新则仅获取新的msg和se
  • 在 Xamarin 中隐藏软键盘

    如何隐藏软键盘以便在聚焦时显示Entry在 Xamarin forms 便携式表单项目中 我假设我们必须为此编写特定于平台的渲染器 但以下内容不起作用 我创建自己的条目子类 public class MyExtendedEntry Entr
  • Jetpack Compose 中复选框中的透明复选标记

    在我的 Compose 应用程序中 我需要创建一个圆形复选框 我已经通过下面的代码实现了这一点 Composable fun CircleCheckBox isChecked Boolean modifier Modifier Modifi
  • Android SHA1 发布密钥库无法与 Google 地图配合使用

    我正在使用 Google Maps Android API 但遇到了一些问题 我正在使用 android studio 签署我的 apk 在 android keystore jks 创建一个 另外 我选择 发布 作为其中的类型 我已经使用
  • 为什么 Google 建议将库复制到您的树中?

    谷歌的Play 服务 API 的使用说明 http developer android com google play services setup html 例如 说 将 extras google google play service
  • Android Studio代理设置构建错误

    每当我尝试在 Android Studio 中构建应用程序时 都会收到以下错误 Error 169 254 16 169 254 16 Will ignore proxy settings for these hosts 我收到错误 5 次
  • Android View Canvas onDraw 未执行

    我目前正在开发一个自定义视图 它在画布上绘制一些图块 这些图块是从多个文件加载的 并将在需要时加载 它们将由 AsyncTask 加载 如果它们已经加载 它们只会被绘制在画布上 这工作正常 如果加载了这些图片 AsyncTask 就会触发v
  • ARM Chromebook 上的 Android 开发环境?

    我尝试了多次安装和使用安卓工作室 https developer android com studio index html on an ARM Chromebook C100P https archlinuxarm org platfor
  • Keystore getEntry 在 Android 9 上返回 NULL

    c我已对存储在 Android 密钥库中的登录密码进行了加密和解密 在 Android 9 上 我观察到应用程序在尝试解密密码时崩溃 我无法重现它 但拥有 Pixel 3 的用户是崩溃的设备之一 下面是我如何从密钥库解密密码 private

随机推荐

  • ERROR in ./src/main.jsModule build failed (from ./node_modules/babel-loader/lib/index.js):Error: e

    ERROR in src main js Module build failed from node modules babel loader lib index js Error error 0308010C digital envelo
  • mysql数据库连接

    一 自带的客户端命令行 直接输入密码即可连接 二 使用口令连接 1 切换目录 输入cd C web mysql 8 0 11 winx64 bin 2 登录 输入mysql u root p 3 输入密码 root 数据库密码 连接成功
  • 【Docker】ubuntu20.04 X86机器搭建NVIDIA ARM64 TX2的Docker镜像

    文章目录 1 设置ubuntu为清华源 1 1 备份源文件 1 2 替换清华源 1 3 更新清华源 2 Ubuntu Docker 安装 3 安装qemu 4 安装Nvidia TX2 Docker镜像 5 如何使用TX2容器 6 参考资料
  • ubuntu下载使用mtcnn和facenet并运行demo

    首先搭建好环境 ubuntu18 04 python3 6 5 tensorflow1 8 0 opencv3 4 3 pip install tqdm为了显示进度条 主要在这两个网站上学习 github上有很多教程和样例 以一般选择星星最
  • Windows使用ssh登入远程服务器(包含mac版)

    windows 首先Windows是没有ssh这个命令的 所以我们先要使Windows可以使用ssh命令 下载openssh for Winodws http linux linuxidc com index php folder MjAx
  • SpringBoot工程使用logback-spring.xml

    在SpringBoot工程中 推荐使用logback spring xml来替换logback xml 原因是SpringBoot加载logback xml是在application yml之前 所以在yml里面的信息不会被logback
  • Python疫情数据可视化分析+数据预测(pandas+pyecharts+statsmodels+matplotlib+sql)

    1 MySQL数据库获取数据 此处的原始数据表是全国各省的实时数据集 现在只获取江苏省份的数据 engine create engine mysq conn 具体内容以自己上就可以为准 select data select from tab
  • C++---类成员变量定义为引用

    摘要 类成员变量是可以定义为引用类型的 但是我们需要注意一下用法 note1 在类中定义引用变量 必须要在初始化列表中初始化该成员变量 const 类型数据成员也必须在初始化列表中进行初始化 include
  • synchronized (成员变量) 和 synchronized (静态成员变量)

    synchronized 成员变量 和 synchronized 静态成员变量 在同步方面有不同的效果 在 Java 中 每个对象都有一个 内置锁 或 对象锁 也称为 monitor 对象 它可以用来同步代码块或方法 使用 synchron
  • 彻底理解数字图像处理中的卷积-以Sobel算子为例

    链接 原文出处 作者 FreeBlues 概述 卷积在信号处理领域有极其广泛的应用 也有严格的物理和数学定义 本文只讨论卷积在数字图像处理中的应用 在数字图像处理中 有一种基本的处理方法 线性滤波 待处理的平面数字图像可被看做一个大矩阵 图
  • 理想的程序员

    http blog jobbole com 84747 我算是靠坑蒙拐骗进了程序员的门 然后一路狂奔 26岁之前几乎没有任何写代码的经验 研究生毕业却意外选择了一家不可能提供培训的初创公司 在每日担忧公司倒闭 害怕被炒鱿鱼以及同事冷落白眼的
  • 偶写的第一个CUDA程序——向量加法

    这个CUDA工程包括了三个文件 一个是makefile 一个是vector add cu 用于联系主机端与设备端 最后是vector add kernel cu 这个就是设备端要执行的真正的CUDA程序 首先看看makefile Add s
  • HarmonyOS开发:探索动态共享包的依赖与使用

    前言 所谓共享包 和Android中的Library本质是一样的 目的是为了实现代码和资源的共享 在HarmonyOS中 给开发者提供了两种共享包 HAR Harmony Archive 静态共享包 和HSP Harmony Shared
  • Can you solve this equation?(二分)

    Problem Description Now given the equation 8 x 4 7 x 3 2 x 2 3 x 6 Y can you find its solution between 0 and 100 Now ple
  • 解决MDK通过JLink或ST-Link烧写程序之后必须断电才可以运行的问题

    问题描述 通过JLink或ST Link烧写完程序之后发现程序不运行 断电重启程序又能跑起来 解决方法 1 打开魔术棒 2 进入Debug界面 点击Setting 3 进入Cortex M Target Driver Setup界面 将 R
  • 详解Hpptclient 与RestTemplate 的Get与Post请求

    spring中最长见得两种请求方式 Get与Post 有些时候我们需要跨域去访问其他服务上的接口 此时就用到HtppClient与RestTemplate 重点讲一下RestTemplate 一 HttpClient 1 引入相关依赖包 m
  • 十、软考2013年下半年软件设计师易错题整理

    十 软考2013年下半年软件设计师易错题整理 文章目录 十 软考2013年下半年软件设计师易错题整理 错题1 错题2 错题3 错题4 错题5 错题6 错题7 错题8 错题9 错题10 错题11 错题12 错题13 错题14 错题15 错题1
  • 单端反激(Flyback)变换器的工作原理

    反激 Flyback 型电路的结构见图2 40 该电路可以看成是将boost buck电路中的电感换成相互耦合的电感N1和N2得到的 因此反激型电路中的变压器在工作中总是经历着储能一放电的过程 电流工作在连续模式CCM 它与正激电路不同的地
  • urdf to webots

    1 webots 模型 文件地址 software workspace git leap model 文件树结构 controller是仿真文件的控制程序 agent revision是正常的控制程序 my controller是调试控制程
  • 利用ScheduledThreadPoolExecutor定时执行任务

    最近时间好忙 终于抽出时间来写一篇博客了 想了想 把之前遇到的一个小bug分享一下吧 之前在做时钟插件时候 我用到了一个定时器 即大家常用的TimerTask 但它总是意外的停止 就是我开启了一个定时器 每一秒都会走run方法 有时候定时器