线程不安全的 SimpleDateFormat
JDK 中提供了 SimpleDateFormat 类,可以用来做日期格式化。这个类有一个令人超级蛋疼的问题:不是线程安全的!!!其实 JDK 文档中有说这件事,但是说的非常含蓄:_
Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.
我在写代码的时候不太喜欢仔细 API 文档,所以就被坑。下面是一个一定会出现并发问题的(代码是 Groovy 写的,比 Java 版简单,可以等价转成 Java):
static void main(String[] ss) {def SDF = new SimpleDateFormat('yyyy-MM-dd')Thread.start {while (true) {def date = SDF.parse("2018-07-27")assert SDF.format(date) == "2018-07-27"}}Thread.start {while (true) {def date = SDF.parse("2016-12-03")assert SDF.format(date) == "2016-12-03"}}}
这段代码呢,你每次执行,都可能抛出不同的异常。真是很不爽。要想愉快的格式化,就要想点别的办法。
补救方法
解法一:朴素做法
每次都 new 一个新的 SimpleDateFormat。直觉告诉你这样做很浪费。这样做很简单,所以就不举例了。
解法二:使用 ThreadLocal
使用 ThreadLocal 就是文档“recommend”的每个线程创建一个单独实例,然后每次使用的时候去 ThreadLocal 中去取就好了。
static ThreadLocal<SimpleDateFormat> sdf
= ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"))
解法三:使用开源库
在 apache commons lang3 这个包中有一个叫 FastDateFormat 的类。它的文档的第一行就是:
FastDateFormat is a fast and thread-safe version of
SimpleDateFormat.
看看,天下苦 SimpleDateFormat 久矣(搞不清楚 JDK 中的 SimpleDateFormat 为啥不改成线程安全的,难道担心有人使用它线程不安全的特性,改完后有兼容问题??)。使用这个类替代 SimpleDateFormat,全局只需要一个对象就够啦。
