众所周知 SimpleDateFormat
线程不安全,很多朋友被其坑过。java
下面是 stackoverflow 的文章 why-is-javas-simpledateformat-not-thread-safe 中的栗子。git
public class ExampleClass {
private static final Pattern dateCreateP = Pattern.compile("Дата подачи:\\s*(.+)");
private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(100);
while (true) {
executor.submit(new Runnable() {
@Override
public void run() {
workConcurrently();
}
});
}
}
public static void workConcurrently() {
Matcher matcher = dateCreateP.matcher("Дата подачи: 19:30:55 03.05.2015");
Timestamp startAdvDate = null;
try {
if (matcher.find()) {
String dateCreate = matcher.group(1);
startAdvDate = new Timestamp(sdf.parse(dateCreate).getTime());
}
} catch (Throwable th) {
th.printStackTrace();
}
System.out.print("OK ");
}
}
复制代码
And result :github
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 OK java.lang.NumberFormatException: For input string: ".201519E.2015192E2"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.nonscalper.webscraper.processor.av.ExampleClass.workConcurrently(ExampleClass.java:37)
at com.nonscalper.webscraper.processor.av.ExampleClass$1.run(ExampleClass.java:25)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
复制代码
每次 new
(实例化) SimpleDateFormat
。web
利用 ThreadLocal
确保每一个线程均可以获得单独的一个 SimpleDateFormat
。apache
public class DateUtil {
private static final ThreadLocal<SimpleDateFormat> local = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String format(Date date) {
return local.get().format(date);
}
public static Date parse(String dateStr) throws ParseException {
return local.get().parse(dateStr);
}
}
复制代码
commons-lang3
中的 FastDateFormat
。<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3-version}</version>
</dependency>
复制代码
性能咋样,jmh 来一把,源码见:github.com/lets-mica/m…安全
# JMH version: 1.21
# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11
Benchmark Mode Cnt Score Error Units
newSimpleDateFormat thrpt 5 114072.841 ± 989.135 ops/s
threadLocal thrpt 5 348207.331 ± 46014.175 ops/s
fastDateFormat thrpt 5 434391.553 ± 7799.593 ops/s
复制代码
结果:fastDateFormat
得分最高。固然你以为这样就完了?ide
在 mica 1.2.1
中咱们利用 Instant
来中转 Date
使用 DateTimeFormatter
格式化。性能
public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
public String format(Date date) {
return DATETIME_FORMATTER.format(date.toInstant());
}
复制代码
注意:DateTimeFormatter
格式化 Instant
须要指定时区。spa
# JMH version: 1.21
# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11
Benchmark Mode Cnt Score Error Units
fastDateFormat thrpt 5 417338.980 56543.104 ops/s
toInstantFormat thrpt 5 371028.709 72059.917 ops/s
复制代码
# JMH version: 1.21
# VM version: JDK 11.0.4, OpenJDK 64-Bit Server VM, 11.0.4+10-b304.69
Benchmark Mode Cnt Score Error Units
fastDateFormat thrpt 5 384637.138 7402.690 ops/s
toInstantFormat thrpt 5 487482.436 12490.986 ops/s
复制代码
使用 DateTimeFormatter
+ Instant
在 java8
下和 commons-lang3
中的 FastDateFormat
已经接近 ,高版本的 jdk
表现突出。 若是你在使用高版本的 jdk
或者考虑后期升级到高版本的 JDK
,该方式都是一个不错的选择。线程
欢迎关注咱们的公众号:如梦技术,精彩内容每日推送。