万能的SimpleDateFormat可以把java.util.Date对象, 或者类似 "2010-11-24 23:23:11.666"的
字符串转换成我们需要的格式或者时间对象。
但是由于时间的概念复杂,又牵扯到时区与本地化,导致了SimpleDateFormat需要处理太多的时间细节,
new一个SimpleDateFormat需要华为太多的时间,这样可能会想到缓存SimpleDateFormat对象
但是万能的SimpleDateFormat恰恰又不是现成安全的。
如果在单线程情况下,缓存SimpleDateFormat对象是不错的选择。
- package com.haitao.utils;
- import java.text.DateFormat;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- public final class SimpleDateFormatUtils {
- public static final String DATE_PARTEN = "yyyy-MM-dd HH:mm:ss.SSS";
- // 静态化缓存
- private static SimpleDateFormat format = new SimpleDateFormat(DATE_PARTEN);
- public static Date cachedParseDate(String str) {
- try {
- return format.parse(str);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- return null;
- }
- public static String cachedFormatDate(Date date) {
- return format.format(date);
- }
- public static Date parseDate(String str) {
- SimpleDateFormat tempFormat = new SimpleDateFormat(DATE_PARTEN);
- try {
- return tempFormat.parse(str);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- return null;
- }
- public static String formatDate(Date date) {
- SimpleDateFormat tempFormat = new SimpleDateFormat(DATE_PARTEN);
- return tempFormat.format(date);
- }
- }
在本机上测试, 10W次 字符串->Date与 10W次 Date> 字符串:
- package com.haitao.test;
- import java.util.Date;
- import com.haitao.utils.SimpleDateFormatUtils;
- public class SimpleDateFormatTest {
- private static int COUNT = 0;
- /**
- * 生成日期字符串数据
- */
- public static String[] genrateDateStr(int count) {
- String[] array = new String[count];
- for(int i = 0; i < count; i++) {
- array[i] = SimpleDateFormatUtils.cachedFormatDate(new Date());
- }
- return array;
- }
- /**
- * 生成日期数据
- */
- public static Date[] genrateDate(int count) {
- String[] strArray = genrateDateStr(count);
- Date[] dateArray = new Date[count];
- for(int i = 0; i < count; i++) {
- dateArray[i] = SimpleDateFormatUtils.cachedParseDate(strArray[i]);
- }
- return dateArray;
- }
- /**
- * 缓存SimpleDateFormat对象, 转换String->Date
- */
- public void cachedParseDateTest(String dateStr) {
- long start = System.currentTimeMillis();
- for(int i = 0; i < COUNT; i++) {
- SimpleDateFormatUtils.cachedParseDate(dateStr);
- }
- long end = System.currentTimeMillis();
- log("cachedParseDate cost:" + (end - start) + "ms.");
- }
- /**
- * 缓存SimpleDateFormat对象, 转换Date->String
- */
- public void cachedFormatDateTest(Date date) {
- long start = System.currentTimeMillis();
- for(int i = 0; i < COUNT; i++) {
- SimpleDateFormatUtils.cachedFormatDate(date);
- }
- long end = System.currentTimeMillis();
- log("cachedFormatDate cost:" + (end - start) + "ms.");
- }
- /**
- * 不缓存转换String->Date
- */
- public void parseDateTest(String dateStr) {
- long start = System.currentTimeMillis();
- for(int i = 0; i < COUNT; i++) {
- SimpleDateFormatUtils.parseDate(dateStr);
- }
- long end = System.currentTimeMillis();
- log("ParseDate cost:" + (end - start) + "ms.");
- }
- /**
- * 不缓存转换Date->String
- */
- public void formatDateTest(Date date) {
- long start = System.currentTimeMillis();
- for(int i = 0; i < COUNT; i++) {
- SimpleDateFormatUtils.formatDate(date);
- }
- long end = System.currentTimeMillis();
- log("formatDate cost:" + (end - start) + "ms.");
- }
- public void log(String message) {
- System.out.println(message);
- }
- public static void main(String[] args) {
- SimpleDateFormatTest sdf = new SimpleDateFormatTest();
- SimpleDateFormatTest.COUNT = 100000;
- String dateStr = "2010-11-20 00:50:42.703";
- sdf.cachedParseDateTest(dateStr);
- sdf.parseDateTest(dateStr);
- sdf.cachedFormatDateTest(new Date());
- sdf.formatDateTest(new Date());
- }
- }
得到如下测试结果:
cachedParseDate cost: 593ms.
ParseDate cost: 1485ms.
cachedFormatDate cost: 328ms.
formatDate cost: 1187ms.
很明显的可以看出通过静态化SimpleDateFormat对象,Date->String 与 String->Date 速度提高了3倍以上.
但是如果在多线程环境下,会造成格式化日期错误, 因此需要借助于ThreadLocal来完成安全的日期格式化:
- package com.haitao.utils;
- import java.text.DateFormat;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- public final class SimpleDateFormatUtils {
- public static final String DATE_PARTEN = "yyyy-MM-dd HH:mm:ss.SSS";
- /**
- * 线程安全转换 String -> Date
- */
- public static Date safeParseDate(String dateStr) {
- try {
- return getFormat().parse(dateStr);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- return null;
- }
- /**
- * 线程安全格式化 Date -> String
- */
- public static String safeFormatDate(Date date) {
- return getFormat().format(date);
- }
- /**
- * 借助ThreadLocal完成对每个线程第一次调用时初始化SimpleDateFormat对象
- */
- private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){
- protected synchronized SimpleDateFormat initialValue() {
- return new SimpleDateFormat(DATE_PARTEN);
- }
- };
- /**
- * 获取当前线程中的安全SimpleDateFormat对象
- */
- private static DateFormat getFormat(){
- return (DateFormat)threadLocal.get();
- }
- }
- package com.haitao.test;
- import java.util.Date;
- import com.haitao.concurrency.FormatDateConcurrencyTask;
- import com.haitao.concurrency.ParseDateConcurrencyTask;
- public class SimpleDateFormatMutiThreadTest {
- private static final int N = 100000;
- public static void main(String[] args) throws Exception {
- Date[] dateArray = SimpleDateFormatTest.genrateDate(N);
- String[] stringArray = SimpleDateFormatTest.genrateDateStr(N);
- // 并发任务, stringArray任务数据, 10000传入每个线程处理任务数据个数
- // 这里会生成10个线程的线程池来处理
- ParseDateConcurrencyTask pdct = new ParseDateConcurrencyTask(stringArray, 10000);
- pdct.run();
- // 并发任务, dataArray任务数据, 10000传入每个线程处理任务数据个数
- // 这里会生成10个线程的线程池来处理
- FormatDateConcurrencyTask fdct = new FormatDateConcurrencyTask(dateArray, 10000);
- fdct.run();
- }
- }