java8+ 簡單、安全、高效的格式化 Date

SimpleDateFormat 線程不安全

衆所周知 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)
複製代碼

解決方案

  1. 每次 new (實例化) SimpleDateFormatweb

  2. 利用 ThreadLocal 確保每一個線程均可以獲得單獨的一個 SimpleDateFormatapache

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);
	}
}
複製代碼
  1. 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

利用 Instant + DateTimeFormatter

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

jdk 8 壓測結果

# 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
複製代碼

jdk 11 壓測結果

# 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 + Instantjava8 下和 commons-lang3 中的 FastDateFormat 已經接近 ,高版本的 jdk 表現突出。 若是你在使用高版本的 jdk 或者考慮後期升級到高版本的 JDK,該方式都是一個不錯的選擇。線程

歡迎關注咱們的公衆號:如夢技術,精彩內容每日推送。

相關文章
相關標籤/搜索