下午在代码review时,和Y同学讨论了SimpleDateFormat的使用,发现自己以前使用有不当的地方,特此记录。
在jdk的doc中指出SimpleDateFormat
Synchronization
Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.
之前使用的时候,直接new一个,没有考虑到多线程问题,在并发的情况下会有问题。
public static DateFormat dataTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
考虑到该方法在工程中用的比较多,测试了几种常用方法的性能,
测试场景:单线程多次format
, 数据如下:
次数:10w
jdkSimpleDataFormat cost time: 1115ms
apacheSimpleDataFormat cost time: 275ms
threadLocalSimpleDataFormat cost time: 183ms
次数:100w
jdkSimpleDataFormat cost time: 4737ms
apacheSimpleDataFormat cost time: 1572ms
threadLocalSimpleDataFormat cost time: 853ms
次数:1000w
jdkSimpleDataFormat cost time: 35170ms
apacheSimpleDataFormat cost time: 14078ms
threadLocalSimpleDataFormat cost time: 8509ms
从数据可以看出
(1) apache common-lang提供的DateFormatUtils.format方法比JDK提供的SimpleDataFormat.format性能高出
149.82%
(2) ThreadLocal方式比 DateFormatUtils.format 方式性能高出 65.44%
结论:在使用SimpleDataFormat时,尽量使用ThreadLocal 方式。
(2) ThreadLocal方式比 DateFormatUtils.format 方式性能高出 65.44%
结论:在使用SimpleDataFormat时,尽量使用ThreadLocal 方式。
测试代码如下,有兴趣的同学可以测下多线程情形下的性能数据。
package com.bc.perf;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.apache.commons.lang.time.DateFormatUtils;
public class SimpleDataFormatPerfTest {
private static final int circleNum = 10000000;
private static String pattern = "yyyy-MM-dd HH:mm:ss";
private Executor testExecutor = Executors.newFixedThreadPool(64);
private static ThreadLocal<SimpleDateFormat> simpleDateFormatProvider = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static void main(String[] args){
SimpleDataFormatPerfTest test = new SimpleDataFormatPerfTest();
SimpleDateFormat format = new SimpleDateFormat(pattern);
System.out.println("jdkSimpleDataFormat date format: " + format.format(new Date()));
System.out.println("apacheSimpleDataFormat date format: " + DateFormatUtils.format(new Date(), pattern));
System.out.println("threadLocalSimpleDataFormat date format: " + simpleDateFormatProvider.get().format(new Date()));
//1 Jdk SimpleDateFormat
test.jdkSimpleDataFormatPerf();
//2 Apache common-lang SimpleDateFormat
test.apacheSimpleDataFormatPerf();
//3 ThreadLocal SimpleDateFormat
test.threadLocalSimpleDataFormatPerf();
}
public void jdkSimpleDataFormatPerf() {
long t1 = System.currentTimeMillis();
for (int i = 0; i < circleNum; i++) {
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.format(new Date());
}
long t2 = System.currentTimeMillis();
System.out.println("originSimpleDataFormat cost time: " + (t2 - t1));
}
public void apacheSimpleDataFormatPerf() {
long t1 = System.currentTimeMillis();
for (int i = 0; i < circleNum; i++) {
DateFormatUtils.format(new Date(), pattern);
}
long t2 = System.currentTimeMillis();
System.out.println("apacheSimpleDataFormat cost time: " + (t2 - t1));
}
public void threadLocalSimpleDataFormatPerf() {
long t1 = System.currentTimeMillis();
for (int i = 0; i < circleNum; i++) {
simpleDateFormatProvider.get().format(new Date());
}
long t2 = System.currentTimeMillis();
System.out.println("threadLocalSimpleDataFormat cost time: " + (t2 - t1));
}
}