SimpleDateformat 线程不安全
SimpleDateFormat 继承自 DateFormat, SimpleDateFormat中的parse方法override父类DateFormat的parse方法。DateFormat的父类提供了public setCalendar的方法 导致潜在的线程安全问题。
- parse方法不安全,使用了实例变量calendar. 当SimpleDateFormat设置为静态变量,多线程calendar发生变化,造成parse中的calendar变量发生了修改。
- format方法不安全,同样是calendar引起的。
解决方案一 jdk7 ThreadLocal
public class MyDateUtil { private static final ThreadLocal<DateFormat> threadSafeFormat = new ThreadLocal<DateFormat>(); private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms"; public static final DateFormat getDateFormat() { // 并发环境下,获取到的是本线程内自己的format DateFormat format = threadSafeFormat.get(); if (format == null) { format = new SimpleDateFormat(MESSAGE_FORMAT); // 并发环境下,将format设置到自己的线程内 threadSafeFormat.set(format); } return format; } }
解决方案二 jdk7 每次new一个新对象
public static final Date getDate(String formatter, String date) { DateTimeFormatter formatter = new DateTimeFormatter(formatter); return formatter.format(date); }
解决方案二 jdk8 DateTimeFormatter 线程安全
DateTimeFormatter内部的static methods 都是返回 new对象或者
public static Date convertStr2Date(String strDate, String dateFormatter, ZoneIdEnum timezone) { DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern(dateFormatter); LocalDate parseDate = LocalDate.parse(strDate,ofPattern); ZoneId zone = ZoneId.of(timezone.getZoneId()); Instant instant = parseDate.atStartOfDay().atZone(zone).toInstant(); return Date.from(instant); }
public enum ZoneIdEnum { AUSTRALIA_DARWIN("Australia/Darwin"), AUSTRALIA_SYDNEY("Australia/Sydney"), AMERICA_ARGENTINA_BUENOS_AIRES("America/Argentina/Buenos_Aires"), AFRICA_CAIRO("Africa/Cairo"), AMERICA_ANCHORAGE("America/Anchorage"), AMERICA_SAO_PAULO("America/Sao_Paulo"), ASIA_DHAKA("Asia/Dhaka"), AFRICA_HARARE("Africa/Harare"), AMERICA_ST_JOHNS("America/St_Johns"), AMERICA_CHICAGO("America/Chicago"), /** * UTC+8 */ ASIA_SHANGHAI("Asia/Shanghai"), AFRICA_ADDIS_ABABA("Africa/Addis_Ababa"), EUROPE_PARIS("Europe/Paris"), AMERICA_INDIANA_INDIANAPOLIS("America/Indiana/Indianapolis"), ASIA_KOLKATA("Asia/Kolkata"), ASIA_TOKYO("Asia/Tokyo"), PACIFIC_APIA("Pacific/Apia"), ASIA_YEREVAN("Asia/Yerevan"), PACIFIC_AUCKLAND("Pacific/Auckland"), ASIA_KARACHI("Asia/Karachi"), AMERICA_PHOENIX("America/Phoenix"), AMERICA_PUERTO_RICO("America/Puerto_Rico"), AMERICA_LOS_ANGELES("America/Los_Angeles"), PACIFIC_GUADALCANAL("Pacific/Guadalcanal"), ASIA_HO_CHI_MINH("Asia/Ho_Chi_Minh"); private final String zoneId; private ZoneIdEnum(String zoneId) { this.zoneId = zoneId ; } public String getZoneId() { return zoneId; } }