SimpleDateFormat线程不安全 作者:马育民 • 2021-07-11 22:42 • 阅读:10132 # 演示 parse() 线程不安全 ``` public class SimpleDateFormat线程不安全 { static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { for(int i=0;i<3;i++) { Task task=new Task(); Thread t = new Thread(task); t.start(); } } static class Task implements Runnable{ @Override public void run() { try { Date date=sdf.parse("2013-05-24 06:02:20"); System.out.println(date); } catch (ParseException e) { e.printStackTrace(); } } } } ``` 报错如下: ``` Exception in thread "Thread-0" Exception in thread "Thread-2" java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) 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:2089) 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 thread.SimpleDateFormat线程不安全$Task.run(SimpleDateFormat线程不安全.java:27) at java.lang.Thread.run(Thread.java:748) java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) 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:2089) 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 thread.SimpleDateFormat线程不安全$Task.run(SimpleDateFormat线程不安全.java:27) at java.lang.Thread.run(Thread.java:748) Fri May 24 06:02:20 CST 2013 ``` ### 错误原因 parse方法实际调用的是 `CalenderBuilder` 的`establish`来进行解析,其方法中主要步骤 **不是原子操作**。 # 演示 format() 线程不安全 ``` public class SimpleDateFormat线程不安全 { static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { for(int i=0;i<30;i++) { Task task=new Task(); Thread t = new Thread(task); t.start(); } } static class Task implements Runnable{ @Override public void run() { String date = sdf.format(new Date()); try { Date parseDate = sdf.parse(date); String date2 = sdf.format(parseDate); System.out.println(date+"---"+date2); System.out.println(date.equals(date2)); } catch (ParseException e) { e.printStackTrace(); } } } } ``` 有一定概率会显示: ``` 2021-07-30 11:22:14---2021-07-30 11:53:14 false ``` ### 错误原因 在 `SimpleDateFormat` 的父类 `DateFormat` 有成员变量 `calendar`,在 调用 `format()`方法时,调用 `calender.setTime` 方法。 因此在多线程环境下,多个线程会同时调用 `calender.setTime` 方法,导致time被别的线程修改,因此线程是不安全的。 # 解决办法 - **在每个方法中** 都创建一个新的 `SimpleDateFormat`对象 - 使用 `ThreadLocal` 对象来保存 `SimpleDateFormat` 对象,详见 [链接](SimpleDateFormat线程不安全-使用ThreadLocal解决 "链接") - 加线程同步锁:synchronized(lock) - 使用DateTimeFormatter代替SimpleDateFormat(java8新特性) 原文出处:http://malaoshi.top/show_1IX1TO9jTyYK.html