java8+ 简单、安全、高效的格式化 Date

SimpleDateFormat 线程不安全

众所周知 SimpleDateFormat 线程不安全,不少朋友被其坑过。

下面是 stackoverflow 的文章 why-is-javas-simpledateformat-not-thread-safe 中的栗子。

1public class ExampleClass { 2 3 private static final Pattern dateCreateP = Pattern.compile("Дата подачи:\\s*(.+)"); 4 private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy"); 5 6 public static void main(String[] args) { 7 ExecutorService executor = Executors.newFixedThreadPool(100); 8 while (true) { 9 executor.submit(new Runnable() { 10 @Override 11 public void run() { 12 workConcurrently(); 13 } 14 }); 15 } 16 } 17 18 public static void workConcurrently() { 19 Matcher matcher = dateCreateP.matcher("Дата подачи: 19:30:55 03.05.2015"); 20 Timestamp startAdvDate = null; 21 try { 22 if (matcher.find()) { 23 String dateCreate = matcher.group(1); 24 startAdvDate = new Timestamp(sdf.parse(dateCreate).getTime()); 25 } 26 } catch (Throwable th) { 27 th.printStackTrace(); 28 } 29 System.out.print("OK "); 30 } 31}

And result :

1OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK java.lang.NumberFormatException: For input string: ".201519E.2015192E2" 2at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043) 3at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) 4at java.lang.Double.parseDouble(Double.java:538) 5at java.text.DigitList.getDouble(DigitList.java:169) 6at java.text.DecimalFormat.parse(DecimalFormat.java:2056) 7at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) 8at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) 9at java.text.DateFormat.parse(DateFormat.java:364) 10at com.nonscalper.webscraper.processor.av.ExampleClass.workConcurrently(ExampleClass.java:37) 11at com.nonscalper.webscraper.processor.av.ExampleClass$1.run(ExampleClass.java:25) 12at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 13at java.util.concurrent.FutureTask.run(FutureTask.java:266) 14at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 15at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 16at java.lang.Thread.run(Thread.java:745)

解决方案

  1. 每次 new (实例化) SimpleDateFormat

  2. 利用 ThreadLocal 确保每个线程都可以得到单独的一个 SimpleDateFormat

    public class DateUtil { private static final ThreadLocal<SimpleDateFormat> local = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    1public static String format(Date date) { 2 return local.get().format(date); 3} 4 5public static Date parse(String dateStr) throws ParseException { 6 return local.get().parse(dateStr); 7}

    }

  3. commons-lang3 中的 FastDateFormat

    <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3-version}</version> </dependency>

性能比拼

性能咋样,jmh 来一把,源码见:https://github.com/lets-mica/mica-jmh

1# JMH version: 1.21 2# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11 3 4Benchmark Mode Cnt Score Error Units 5newSimpleDateFormat thrpt 5 114072.841 ± 989.135 ops/s 6threadLocal thrpt 5 348207.331 ± 46014.175 ops/s 7fastDateFormat thrpt 5 434391.553 ± 7799.593 ops/s

结果:fastDateFormat 得分最高。当然你觉得这样就完了?

利用 Instant + DateTimeFormatter

mica 1.2.1 中我们利用 Instant 来中转 Date 使用 DateTimeFormatter 格式化。

1public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); 2 3public String format(Date date) { 4 return DATETIME_FORMATTER.format(date.toInstant()); 5}

注意:DateTimeFormatter 格式化 Instant 需要指定时区

jdk 8 压测结果

1# JMH version: 1.21 2# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11 3 4Benchmark Mode Cnt Score Error Units 5fastDateFormat thrpt 5 417338.980 56543.104 ops/s 6toInstantFormat thrpt 5 371028.709 72059.917 ops/s

jdk 11 压测结果

1# JMH version: 1.21 2# VM version: JDK 11.0.4, OpenJDK 64-Bit Server VM, 11.0.4+10-b304.69 3 4Benchmark Mode Cnt Score Error Units 5fastDateFormat thrpt 5 384637.138 7402.690 ops/s 6toInstantFormat thrpt 5 487482.436 12490.986 ops/s

结论

使用 DateTimeFormatter + Instantjava8 下和 commons-lang3 中的 FastDateFormat 已经接近 ,高版本的 jdk 表现突出。 如果你在使用高版本的 jdk 或者考虑后期升级到高版本的 JDK,该方式都是一个不错的选择。

欢迎关注我们的公众号:如梦技术,精彩内容每日推送。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java爬虫之JSoup使用教程

title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin

Java8为什么提供LocalDate、LocalTime、LocalDateTime 时间类

Java8为什么提供LocalDate、LocalTime、LocalDateTime时间类?Date不格式化打印可读性差。TueSep1009:34:04CST2019使用SimpleDateFormat对时间进行格式化,但SimpleDateFormat是线程不安全

java8+ 简单、安全、高效的格式化 Date - HelloWorld