1、ScheduledExecutorService 和 Timer 的区别
Timer的内部只有一个线程,如果有多个任务的话就会顺序执行,这样我们的延迟时间和循环时间就会出现问题。
ScheduledExecutorService是线程池,所以就不会出现这个情况,在对延迟任务和循环任务要求严格的时候,就需要考虑使用ScheduledExecutorService了。
2、简单掌握下在多线程中的“安全格式化对象”
在以前的数据格式化使用中我们习惯性的使用:SimpleDateFormat,可是在多线程的环境下使用可能会出现安全问题、
- 为什么SimpleDateFormat不是安全的?
进入parse源码中: 根据传入的日期时间字符串来解析,然后将解析好的日期数据设置到calendar中,也就是通过establish方法完成的, 这个方法中调用了cal.clear(),这将会导致calendar中的属性值变为初始值, 如果在多线程并发的情况下,有可能线程A刚执行完establish方法,线程B就执行了cal.clear(),导致最终的解析异常。
- 为什么在多线程中通常使用ThreadLocal来包装一下SimpleDataFormat?
如果不使用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 }