求求你别用SimpleDateFormat了!

前言git

 

啊哈哈,标题写的比较随意了,其实呢最近在各类面试以及博客中,SimpleDateFormat出镜率确实是比较高了,为何?其实聪明的大家确定知道,那必须是有坑呗,是的,那咱们就以案例来分析一下到底会有那些坑,或者还有没有其余更优的替代方案呢?github

 

正文面试

 

首先咱们来看一下可能会出如今DateUtils中的写法:多线程

private static final SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss"); public static Date formatDate(String date) throws ParseException { return dayFormat.parse(date); }

 

当咱们在单线程的程序中调用 formatDate(date) ,此时并不会出现任何问题(若是这也出问题那还玩什么...) ,然而当咱们的程序在多线程并发执行调用这个方法的时候。并发

 

 

ExecuterService es = ExecuterService.newFixedThreadPool(50); for( ... ){ es.execute( () -> { System.out.println(parse("2018-11-11 10:35:20")); }) }

 

此时你会发现打印出来的时间有些是错误,程序甚至会抛出异常NumberFormatException??为何会出现这种状况呢?咱们能够直接查看SimpleDateFormat.parse() 方法的源码一探究竟。spa

 

private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list
 calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i < compiledPattern.length; ) { int tag = compiledPattern\[i\] >>> 8; int count = compiledPattern\[i++\] & 0xff; if (count == 255) { count = compiledPattern\[i++\] << 16; count |= compiledPattern\[i++\]; }

 

从源码能够看到,在多线程的环境中,执行到第五行 calendar进行操做的时候,后面的线程有可能会覆盖上一个线程设置好的值,此时就致使前面线程执行的结果被覆盖而返回了一个错误的值。线程

 

那咱们该如何避免这个坑呢?code

 

一、使用ThreadLocal,每一个线程中返回各自的实例,避免了多线程环境中共用同一个实例而致使的问题。orm

private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<>(); public static Date formatDate(String date) throws ParseException { SimpleDateFormat dayFormat = getSimpleDateFormat(); return dayFormat.parse(date); } private static SimpleDateFormat getSimpleDateFormat() { SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get(); if (simpleDateFormat == null){ simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd  HH:mm:ss"); simpleDateFormatThreadLocal.set(simpleDateFormat); } return simpleDateFormat; }

 

须要注意一点的是,ThreadLocal的使用过程当中也是有小坑须要注意的,你们能够参考一下其余的资料,之后能够抽空聊聊这个话题。

 

二、推荐升级到JDK8+,使用LocalDateTime,LocalDate,LocalTime来代替,具体的用法请自行参考API,固然JDK8所带来的Lambda,Stream等特性也是值得一试的。blog

 

三、使用三方包,推荐Joda-Time,对于日期的增减操做也是至关便捷。

 

github:https://github.com/JodaOrg/joda-time

相关文章
相关标签/搜索