衆所周知 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
,該方式都是一個不錯的選擇。線程
歡迎關注咱們的公衆號:如夢技術,精彩內容每日推送。