万能的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倍以上的执行效率:)
相关推荐
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); date.setTime(time); System.out.println(sdf.format(date)); 发现时间于想要的时间不符,请运行Time.reg文件
JavaScript实现的java.text.SimpleDateFormat。希望多多交流。
有关SimpleDateFormat的常用方法说明
日期操作。。。基础的SimpleDateFormat格式化日期!!操作!》初级学习代码
由浅入深解析 SimpleDateFormat 由浅入深解析 SimpleDateFormat
java 使用SimpleDateFormat类获取系统的当前时间 java 使用SimpleDateFormat类获取系统的当前时间
SimpleDateFormat使用详解。非常实用!!!!
目录SimpleDateFormat诡异bug复现SimpleDateFormat诡异bug字符串日期转Date日期(parse)Date日期转String类型(format)SimpleDateFormat出现bug的原因如何解决SimpleDateFormat多线程安全问题局部变量使用...
高并发之-SimpleDateFormat类的线程安全问题和解决方案.docx
深入理解Java:SimpleDateFormat安全的时间格式化
1.创建SimpleDateFormat对象,确定日期被格式化的格式 2.使用循环,在循环中调用Thread的sleep方法,让线程休眠1s后打印当前时间的字符串
NULL 博文链接:https://qinchaoyong.iteye.com/blog/543109
NULL 博文链接:https://chaoyi.iteye.com/blog/2082317
NULL 博文链接:https://flynndang.iteye.com/blog/711878
SimpleDateFormat里面对应格式化输出的模式字母
比Hashmap性能高的map 比simpleDateFormat性能高的java日期控件
SimpleDateFormat 是一个以国别敏感的方式格式化和分析数据的具体类。这篇文章主要介绍了Java中的SimpleDateFormat使用详解,需要的朋友可以参考下
主要为大家详细介绍了Java SimpleDateFormat中英文时间格式化转换,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
SimpleDateFormat线程不安全的5种解决方案.md