SimpleDateFormat线程安全问题和解决方案
Posted 泡^泡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SimpleDateFormat线程安全问题和解决方案相关的知识,希望对你有一定的参考价值。
为什么线程不安全
在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
public StringBuffer format(Date date, StringBuffer toAppendTo,FieldPosition pos)
pos.beginIndex = pos.endIndex = 0;
return format(date,toAppendTo,pos.getFieldDelegate());
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date,StringBuffer toAppendTo,FieldDelegate delegate)
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length;)
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255)
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
switch (tag)
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
return toAppendTo;
protected Calendar calendar;
可以看到,多个线程之间共享变量calendar,并修改calendar。因此在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
此外,parse方法也是线程不安全的,parse方法实际调用的是CalenderBuilder的establish来进行解析,其方法中主要步骤不是原子操作。
解决方案
- 使用ThreadLocal,每个线程都拥有自己的SimpleDateFormat对象副本
- 加一把线程同步锁:synchronized(lock)
- 将SimpleDateFormat定义成局部变量
- 使用DateTimeFormatter代替SimpleDateFormat
,DateTimeFormatter是线程安全的,默认提供了很多格式化方法,也可以通过ofPattern方法创建自定义格式化方法。
package com;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleDateFormatTest
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal();
private static DateFormat getDateFormat()
//从当前线程的范围内获得一个DateFormat
DateFormat dateFormat = threadLocal.get();
if(dateFormat == null)
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//Thread.currentThread范围内设置一个SimpleDateFormat
threadLocal.set(dateFormat);
return dateFormat;
public static Date parse(String strDate) throws ParseException
return getDateFormat().parse(strDate);
public static void main(String[] args)
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 20;i++)
executorService.execute(()->
try
System.out.println(parse("2022-07-09 10:00:00"));
catch (ParseException e)
e.printStackTrace();
);
格式化日期示例:
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2019-11-20T15:04:29.017
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String strDate=localDateTime.format(dtf);
System.out.println(strDate); // 2019/23/20 15:23:46
解析日期:
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
System.out.println(localDateTime); // 2019-11-20T15:23:46
以上是关于SimpleDateFormat线程安全问题和解决方案的主要内容,如果未能解决你的问题,请参考以下文章