定时器任务:Timer跟ScheduledExecutorService
1、ScheduledExecutorService 和 Timer 的区别
Timer的内部只有一个线程,如果有多个任务的话就会顺序执行,这样我们的延迟时间和循环时间就会出现问题。
ScheduledExecutorService是线程池,所以就不会出现这个情况,在对延迟任务和循环任务要求严格的时候,就需要考虑使用ScheduledExecutorService了。
2、简单掌握下在多线程中的“安全格式化对象”
在以前的数据格式化使用中我们习惯性的使用:SimpleDateFormat,可是在多线程的环境下使用可能会出现安全问题、
- 为什么SimpleDateFormat不是安全的?
1 2 3 4 | 进入parse源码中: 根据传入的日期时间字符串来解析,然后将解析好的日期数据设置到calendar中,也就是通过establish方法完成的, 这个方法中调用了cal.clear(),这将会导致calendar中的属性值变为初始值, 如果在多线程并发的情况下,有可能线程A刚执行完establish方法,线程B就执行了cal.clear(),导致最终的解析异常。 |
- 为什么在多线程中通常使用ThreadLocal来包装一下SimpleDataFormat?
1 2 3 | 如果不使用ThreadLocal包装一下,直接创建一个SimpleDateFormat共享实例对象, 在多线程并发的情况下使用这个对象的方法是线程不安全的,可能会抛出NumberFormatException或其它异常。 使用ThreadLocal包装一下,每个线程都有自己的SimpleDateFormat实例对象,这样多线程并发的情况下就不会出现线程不安全的问题了。 |
- 包装后的代码
1 // 创建安全的数据格式化对象 2 static ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { 3 @Override 4 protected DateFormat initialValue() { 5 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 6 } 7 };
3、Timer跟ScheduleExecutorService创建定时任务对比
Timer定时器:
public class Test1 { public static void main(String[] args) throws ParseException { //1、创建定时器对象 Timer timer = new Timer(); //2、指定定时任务 /** * 参数1:定时任务 * 参数2:第一次执行时间 * 参数3:多久执行一次,单位:毫秒 */ timer.schedule( new Login(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2021-02-02 15:22:00"),//parse:日期转时间 5000 ); } } //记录日志,继承TimerTask指定定时任务 class Login extends TimerTask{ @Override public void run() { SimpleDateFormat sim = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // format:时间转日期 String time = sim.format(new Date()); System.out.println(time+"成功备份一次数据"); } }
ScheduleExecutorService定时器:
1 public class test12_定时器任务plus { 2 // 创建安全的数据格式化对象 3 static ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { 4 @Override 5 protected DateFormat initialValue() { 6 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 7 } 8 }; 9 10 public static void main(String[] args) { 11 // 在线程池创建新的线程 12 ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1); 13 14 /** 15 * 参数1:要执行的任务 16 * 参数2:多长时间后执行任务 17 * 参数1:执行任务的时间间隔 18 * 参数4:延迟参数的时间单位 19 */ 20 executorService.scheduleWithFixedDelay( 21 new TimerTask() { 22 @Override 23 public void run() { 24 System.out.println(df.get().format(new Date()) + "成功备份一次数据"); 25 } 26 }, 27 // 当前时间执行任务,每3s执行一次 28 0, 29 3, 30 TimeUnit.SECONDS 31 ); 32 } 33 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话