一般情况下:
在日常开发中,我们会把SimpleDateFormat定义为一个静态变量,以此来避免频繁创建对象实例,但是在多线程的情况下,SimpleDateFormat实例就会被多个线程共享,会出现各种各样的问题。
解决方案:
1、使用synchronized,非常简单的解决了线程安全的问题,缺陷就是synchronized是重量级的,并发量大的时候对性能有影响。
2、在需要的时候创建新的实例,不用static修饰
public static String formatDate(Date date) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(strDate);
}
如上代码,仅在需要用到的地方创建一个新的实例,就没有线程安全问题,不过也加重了创建对象的负担,会频繁地创建和销毁对象,效率较低。
3、使用ThreadLocal
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
ThreadLocal可以确保每个线程都可以得到单独的一个SimpleDateFormat的对象,那么自然也就不存在竞争问题了。
4、基于JDK1.8的DateTimeFormatter
在jdk1.8中,可以使用instant代替Date,LocalDateTime 代替 Calendar,DateTimeFormatter代替simpleDateFormat,
public class SimpleDateFormatTest {
//DateTimeFormatter的类是不可变的,并且是线程安全的。
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate(LocalDateTime date) {
return formatter.format(date);
}
public static LocalDateTime parse(String dateNow) {
return LocalDateTime.parse(dateNow, formatter);
}
public static void main(String[] args) throws Exception {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(50);
// 20个线程
for (int i = 0; i < 20; i++) {
service.execute(() -> {
for (int j = 0; j < 10; j++) {
try {
System.out.println(parse(formatDate(LocalDateTime.now())));
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
service.shutdown();
service.awaitTermination(1, TimeUnit.DAYS);
}
}
阿里开发手册中:
private static final ThreadLocal<DateFormat> sdf = new ThreadLocal<DateFormat>(){
@override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
}