`
tuhaitao
  • 浏览: 375605 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

SimpleDateFormat性能调优

    博客分类:
  • java
阅读更多

万能的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();
    }

}
 
测试方法,同样使用10W次 字符串->Date与 10W次 Date> 字符串:

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();
		
		
	}
}
 
并发的代码就不贴了,这个我自己写了一个单机的mapReduce并发任务框架,太乱,还没来得及整理,有时间给大家分享一下.

测试结果如下:

safeParseDate cost:359ms.
safeFormatDate cost:297ms.

可以看到测试结果比单线程cached模型都效率高,当然这里是由于多线程处理,在所有线程执行完毕进行最后统计,所以速度会这么快,在单线程效果下会比cached模型略微低一点,大概50ms左右的样子.

优化完毕,结论是通过在当前线程内缓存SimpleDateFormat既可以达到线程安全,又可以提升3倍以上的执行效率:)


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics