SimpleDateFormat 线程不安全问题及解决方案
Posted wenniuwuren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SimpleDateFormat 线程不安全问题及解决方案相关的知识,希望对你有一定的参考价值。
零、概述
任何线程不安全的问题,其实本质就是共用了一份数据且没有进行加锁同步,SimpleDateFormat 也是一样。
一、错误案例
public class Test
static DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws Exception
ExecutorService ts = Executors.newFixedThreadPool(1000);
for (;;)
ts.execute(new Runnable()
@Override
public void run()
try
String format = df.format(new Date(Math.abs(new Random().nextLong())));
System.out.println(format);
catch (Exception e)
e.printStackTrace();
System.exit(1);
);
运行后出现报错:以下只是报错的一种,多线程场景下会出现多种不同的错误
192456611-09-28 19:38:41
26312543-11-28 13:35:35
233029168-10-09 00:28:24
100149768-11-04 22:59:40
141560024-07-30 14:30:46
52461160-03-13 16:40:29
131434392-08-03 13:32:17
246393230-02-21 08:50:04
71490728-12-01 23:32:16
284007186-04-28 14:34:21
184430351-11-02 17:30:38
49670911-09-27 20:43:33java.lang.ArrayIndexOutOfBoundsException: -2850986
at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:453)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2397)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2312)
at java.util.Calendar.complete(Calendar.java:2268)
at java.util.Calendar.get(Calendar.java:1826)
at java.text.SimpleDateFormat.subFormat(SimpleDateFormat.java:1119)
at java.text.SimpleDateFormat.format(SimpleDateFormat.java:966)
at java.text.SimpleDateFormat.format(SimpleDateFormat.java:936)
at java.text.DateFormat.format(DateFormat.java:345)
at test.Test$1.run(Test.java:102)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
SimpleDateFormat 注释里面就描述了,此类为非线程安全的,所以上述用了 static 或者定义为全局变量在多线程场景就会出现问题。
二、原理分析
SimpleDateFormat.java
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate)
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
......
可以看到传入的 Date 是赋值在全局变量 calendar 上的,那么在多线程场景,calendar 就会被覆盖产生数据错乱,数组越界等问题。
三、解决方案
public class Test
static DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws Exception
ExecutorService ts = Executors.newFixedThreadPool(100);
for (;;)
ts.execute(new Runnable()
@Override
public void run()
try
Instant instant = new Date(Math.abs(new Random().nextLong())).toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
System.out.println(df.format(localDateTime));
catch (Exception e)
e.printStackTrace();
System.exit(1);
);
上述使用 JDK1.8 的方案,实现类是线程安全的,就解决了问题。
当然还有其他方案,比如
1、将SimpleDateFormat定义成局部变量
缺点:定义为局部对象,每调用一次方法就会创建一个 SimpleDateFormat 对象,增加GC对象
2、方法加同步锁synchronized,在同一时刻,只有一个线程可以执行类中的某个方法
缺点:性能较差
以上是关于SimpleDateFormat 线程不安全问题及解决方案的主要内容,如果未能解决你的问题,请参考以下文章
Java Review - SimpleDateFormat线程不安全原因的源码分析及解决办法