首先简单说一下SimpleDateFormat存在线程安全问题的原因。
SimpleDateFormat继承了DateFormat类,类中有一个受保护类型的Calendar对象,看一下SimpleDateFormat的format方法:
// 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);
......
}
return toAppendTo;
多个线程共享SimpleDateFormat实例时,对Calendar对象的更改会相互影响,因此产生线程安全问题。
解决方案:
1、每当使用时创建一个SimpleDateFormat的实例,但性能会较差(其实以现在jdk的优异表现和软硬件性能的提升,
创建SimpleDateFormat实例对性能的影响并不是很明显)
2、使用ThreadLocal避免多线程共享实例。(推荐)
实现如下
import org.apache.commons.collections.map.LRUMap;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
public class DateFormatUtil {
private static final int MAX_SDF_SIZE = 20;
// 不一定要LRUMap,任意Map都行
private static final ThreadLocal<LRUMap> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<LRUMap>() {
@Override
protected LRUMap initialValue() {
return new LRUMap(MAX_SDF_SIZE);
}
};
public static SimpleDateFormat getDateFormat(String pattern) {
LRUMap sdfMap = DATE_FORMAT_THREAD_LOCAL.get();
SimpleDateFormat sdf = (SimpleDateFormat) sdfMap.get(pattern);
if (sdf == null) {
sdf = new SimpleDateFormat(pattern);
sdf.setLenient(false);
sdfMap.put(pattern, sdf);
}
return sdf;
}
public static void destory() {
DATE_FORMAT_THREAD_LOCAL.remove();
}
public static Date parse4y2M2d(String source) {
try {
return getDateFormat("yyyy-MM-dd").parse(source);
} catch (Exception e) {
return null;
}
}
public static Date parseyyyyMMdd(String source) {
try {
return getDateFormat("yyyyMMdd").parse(source);
} catch (Exception e) {
return null;
}
}
public static Date parseyyyyMMddHHmmss(String source) {
try {
return getDateFormat("yyyyMMddHHmmss").parse(source);
} catch (Exception e) {
return null;
}
}
public static Date parse4y2M2d2h2m2s(String source) {
try {
return getDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);
} catch (Exception e) {
return null;
}
}
public static Date parse4y2M2d2h2m(String source) {
try {
return getDateFormat("yyyy-MM-dd HH:mm").parse(source);
} catch (Exception e) {
return null;
}
}
public static Date parse4y2M2dT2h2m2s(String source) {
try {
return getDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(source);
} catch (Exception e) {
return null;
}
}
public static Date parse4y2M2dhhmm(String source) {
try {
return getDateFormat("yyyy-MM-dd HHmm").parse(source);
} catch (Exception e) {
return null;
}
}
public static String format4y2M2d(Date date) {
if (Objects.isNull(date)){
return null;
}
return getDateFormat("yyyy-MM-dd").format(date);
}
public static String format4y2M2dCN(Date date) {
return getDateFormat("yyyy年MM月dd日").format(date);
}
public static String format2M2dCN(Date date) {
return getDateFormat("MM月dd日").format(date);
}
public static String formatMdCN(Date date) {
return getDateFormat("M月d日").format(date);
}
public static String formatyyyyMMdd(Date date) {
return getDateFormat("yyyyMMdd").format(date);
}
public static String parseyyyyMMddHHmmss(Date date) {
return getDateFormat("yyyyMMddHHmmss").format(date);
}
public static String format4y2M2d2h2m2s(Date date) {
if (Objects.isNull(date)){
return null;
}
return getDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
public static String formatyyyyMMddHHmmss(Date date) {
return getDateFormat("yyyyMMddHHmmss").format(date);
}
public static String format4y2M2d2h2m(Date date) {
return getDateFormat("yyyy-MM-dd HH:mm").format(date);
}
public static String format2h2m2s(Date date) {
return getDateFormat("HH:mm:ss").format(date);
}
public static String format2h2m(Date date) {
return getDateFormat("HH:mm").format(date);
}
public static String formathhmm(Date date) {
return getDateFormat("HHmm").format(date);
}
private static Calendar getCalendar(String pattern) {
LRUMap sdfMap = DATE_FORMAT_THREAD_LOCAL.get();
if (sdfMap.containsKey(pattern)) {
return (Calendar) sdfMap.get(pattern);
} else {
Calendar cal = Calendar.getInstance();
sdfMap.put(pattern, cal);
return cal;
}
}
public static Date parseLong2Date(long longTime) {
Calendar cal = getCalendar("long2Date");
cal.clear();
cal.setTimeInMillis(longTime);
return cal.getTime();
}
public static Date parseHHmm(String source) {
try {
return getDateFormat("HH:mm").parse(source);
} catch (Exception e) {
return null;
}
}
}