`

SimpleDateFormat性能调优(转载)

 
阅读更多

万能的SimpleDateFormat可以把java.util.Date对象, 或者类似 "2010-11-24 23:23:11.666"的

 

字符串转换成我们需要的格式或者时间对象。

 

但是由于时间的概念复杂,又牵扯到时区与本地化,导致了SimpleDateFormat需要处理太多的时间细节,

new一个SimpleDateFormat需要华为太多的时间,这样可能会想到缓存SimpleDateFormat对象

但是万能的SimpleDateFormat恰恰又不是现成安全的。

 

如果在单线程情况下,缓存SimpleDateFormat对象是不错的选择。

 

 

Java代码  收藏代码
  1. package com.haitao.utils;  
  2.   
  3. import java.text.DateFormat;  
  4. import java.text.ParseException;  
  5. import java.text.SimpleDateFormat;  
  6. import java.util.Date;  
  7.   
  8. public final class SimpleDateFormatUtils {  
  9.       
  10.     public static final String DATE_PARTEN = "yyyy-MM-dd HH:mm:ss.SSS";  
  11.         // 静态化缓存  
  12.     private static SimpleDateFormat format = new SimpleDateFormat(DATE_PARTEN);  
  13.       
  14.     public static Date cachedParseDate(String str) {  
  15.         try {  
  16.             return format.parse(str);  
  17.         } catch (ParseException e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.         return null;  
  21.     }  
  22.       
  23.     public static String cachedFormatDate(Date date) {  
  24.         return format.format(date);  
  25.     }  
  26.       
  27.     public static Date parseDate(String str) {  
  28.         SimpleDateFormat tempFormat = new SimpleDateFormat(DATE_PARTEN);  
  29.         try {  
  30.             return tempFormat.parse(str);  
  31.         } catch (ParseException e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.         return null;  
  35.     }  
  36.       
  37.     public static String formatDate(Date date) {  
  38.         SimpleDateFormat tempFormat = new SimpleDateFormat(DATE_PARTEN);  
  39.         return tempFormat.format(date);  
  40.     }  
  41.       
  42. }  
 

在本机上测试, 10W次 字符串->Date与 10W次 Date> 字符串:

 

 

Java代码  收藏代码
  1. package com.haitao.test;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import com.haitao.utils.SimpleDateFormatUtils;  
  6.   
  7. public class SimpleDateFormatTest {  
  8.       
  9.     private static int COUNT = 0;  
  10.       
  11.     /** 
  12.      * 生成日期字符串数据 
  13.      */  
  14.     public static String[] genrateDateStr(int count) {  
  15.         String[] array = new String[count];  
  16.         for(int i = 0; i < count; i++) {  
  17.             array[i] = SimpleDateFormatUtils.cachedFormatDate(new Date());  
  18.         }  
  19.         return array;  
  20.     }  
  21.       
  22.     /** 
  23.      * 生成日期数据 
  24.      */  
  25.     public static Date[] genrateDate(int count) {  
  26.         String[] strArray = genrateDateStr(count);  
  27.         Date[] dateArray = new Date[count];  
  28.         for(int i = 0; i < count; i++) {  
  29.             dateArray[i] = SimpleDateFormatUtils.cachedParseDate(strArray[i]);  
  30.         }  
  31.         return dateArray;  
  32.     }  
  33.       
  34.     /** 
  35.      * 缓存SimpleDateFormat对象, 转换String->Date 
  36.      */  
  37.     public void cachedParseDateTest(String dateStr) {  
  38.         long start = System.currentTimeMillis();  
  39.         for(int i = 0; i < COUNT; i++) {  
  40.             SimpleDateFormatUtils.cachedParseDate(dateStr);  
  41.         }  
  42.         long end = System.currentTimeMillis();  
  43.         log("cachedParseDate cost:" + (end - start) + "ms.");  
  44.     }  
  45.       
  46.     /** 
  47.      * 缓存SimpleDateFormat对象, 转换Date->String 
  48.      */  
  49.     public void cachedFormatDateTest(Date date) {  
  50.         long start = System.currentTimeMillis();  
  51.         for(int i = 0; i < COUNT; i++) {  
  52.             SimpleDateFormatUtils.cachedFormatDate(date);  
  53.         }  
  54.         long end = System.currentTimeMillis();  
  55.         log("cachedFormatDate cost:" + (end - start) + "ms.");  
  56.     }  
  57.       
  58.     /** 
  59.      * 不缓存转换String->Date 
  60.      */  
  61.     public void parseDateTest(String dateStr) {  
  62.         long start = System.currentTimeMillis();  
  63.         for(int i = 0; i < COUNT; i++) {  
  64.             SimpleDateFormatUtils.parseDate(dateStr);  
  65.         }  
  66.         long end = System.currentTimeMillis();  
  67.         log("ParseDate cost:" + (end - start) + "ms.");  
  68.     }  
  69.       
  70.     /** 
  71.      * 不缓存转换Date->String 
  72.      */  
  73.     public void formatDateTest(Date date) {  
  74.         long start = System.currentTimeMillis();  
  75.         for(int i = 0; i < COUNT; i++) {  
  76.             SimpleDateFormatUtils.formatDate(date);  
  77.         }  
  78.         long end = System.currentTimeMillis();  
  79.         log("formatDate cost:" + (end - start) + "ms.");  
  80.     }  
  81.       
  82.       
  83.     public void log(String message) {  
  84.         System.out.println(message);  
  85.     }  
  86.       
  87.     public static void main(String[] args) {  
  88.         SimpleDateFormatTest sdf = new SimpleDateFormatTest();  
  89.         SimpleDateFormatTest.COUNT = 100000;  
  90.           
  91.         String dateStr = "2010-11-20 00:50:42.703";  
  92.         sdf.cachedParseDateTest(dateStr);  
  93.         sdf.parseDateTest(dateStr);  
  94.           
  95.         sdf.cachedFormatDateTest(new Date());  
  96.         sdf.formatDateTest(new Date());  
  97.           
  98.     }  
  99.       
  100. }  
 

 

得到如下测试结果:

 

 

cachedParseDate   cost: 593ms.

ParseDate              cost: 1485ms.

cachedFormatDate cost: 328ms.

formatDate             cost: 1187ms.

 

很明显的可以看出通过静态化SimpleDateFormat对象,Date->String 与 String->Date 速度提高了3倍以上.

 

 

但是如果在多线程环境下,会造成格式化日期错误, 因此需要借助于ThreadLocal来完成安全的日期格式化:

 

 

Java代码  收藏代码
  1. package com.haitao.utils;  
  2.   
  3. import java.text.DateFormat;  
  4. import java.text.ParseException;  
  5. import java.text.SimpleDateFormat;  
  6. import java.util.Date;  
  7.   
  8. public final class SimpleDateFormatUtils {  
  9.       
  10.     public static final String DATE_PARTEN = "yyyy-MM-dd HH:mm:ss.SSS";  
  11.       
  12.     /** 
  13.      * 线程安全转换 String -> Date 
  14.      */  
  15.     public static Date safeParseDate(String dateStr) {  
  16.         try {  
  17.             return getFormat().parse(dateStr);  
  18.         } catch (ParseException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.         return null;  
  22.     }  
  23.       
  24.     /** 
  25.      * 线程安全格式化 Date -> String 
  26.      */  
  27.     public static String safeFormatDate(Date date) {  
  28.         return getFormat().format(date);  
  29.     }  
  30.     
  31.     /** 
  32.      * 借助ThreadLocal完成对每个线程第一次调用时初始化SimpleDateFormat对象 
  33.      */  
  34.     private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){  
  35.         protected synchronized SimpleDateFormat initialValue() {  
  36.             return new SimpleDateFormat(DATE_PARTEN);  
  37.         }  
  38.     };  
  39.     
  40.     /** 
  41.      * 获取当前线程中的安全SimpleDateFormat对象 
  42.      */  
  43.     private static DateFormat getFormat(){  
  44.         return (DateFormat)threadLocal.get();  
  45.     }  
  46.   
  47. }  
 
测试方法,同样使用10W次 字符串->Date与 10W次 Date> 字符串:
 
Java代码  收藏代码
  1. package com.haitao.test;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import com.haitao.concurrency.FormatDateConcurrencyTask;  
  6. import com.haitao.concurrency.ParseDateConcurrencyTask;  
  7.   
  8. public class SimpleDateFormatMutiThreadTest {  
  9.       
  10.     private static final int N = 100000;  
  11.       
  12.     public static void main(String[] args) throws Exception {  
  13.         Date[] dateArray = SimpleDateFormatTest.genrateDate(N);  
  14.         String[] stringArray = SimpleDateFormatTest.genrateDateStr(N);  
  15.           
  16.         // 并发任务, stringArray任务数据, 10000传入每个线程处理任务数据个数  
  17.         // 这里会生成10个线程的线程池来处理  
  18.         ParseDateConcurrencyTask pdct = new ParseDateConcurrencyTask(stringArray, 10000);  
  19.         pdct.run();  
  20.         // 并发任务, dataArray任务数据, 10000传入每个线程处理任务数据个数  
  21.         // 这里会生成10个线程的线程池来处理  
  22.         FormatDateConcurrencyTask fdct = new FormatDateConcurrencyTask(dateArray, 10000);  
  23.         fdct.run();  
  24.           
  25.           
  26.     }  
  27. }  
 
并发的代码就不贴了,这个我自己写了一个单机的mapReduce并发任务框架,太乱,还没来得及整理,有时间给大家分享一下.
 
测试结果如下:
 
safeParseDate cost:359ms.
safeFormatDate cost:297ms.
 
可以看到测试结果比单线程cached模型都效率高,当然这里是由于多线程处理,在所有线程执行完毕进行最后统计,所以速度会这么快,在单线程效果下会比cached模型略微低一点,大概50ms左右的样子.
 
优化完毕,结论是通过在当前线程内缓存SimpleDateFormat既可以达到线程安全,又可以提升3倍以上的执行效率:)
 
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics