`

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倍以上的执行效率:)
 
 
分享到:
评论

相关推荐

    JavaScript实现的SimpleDateFormat

    在处理复杂格式时,可能会有性能上的考虑,因为正则表达式在处理大量数据时可能会变得效率低下。此外,为了兼容性,这个实现可能还需要处理不同的区域设置和语言环境。 在实际项目中,除了自定义实现外,还可以考虑...

    高并发之-SimpleDateFormat类的线程安全问题和解决方案.docx

    SimpleDateFormat类的线程安全问题是因为它使用了缓存机制来提高解析和格式化的性能。缓存机制使用了一个缓存数组来存储解析和格式化的结果,但是这个缓存数组是共享的,这意味着在多线程环境中,多个线程可能会同时...

    由浅入深解析 SimpleDateFormat

    SimpleDateFormat 详解 SimpleDateFormat 是 Java 语言中的一种日期和时间格式化类,用于将日期和时间格式...通过合理地使用 SimpleDateFormat,我们可以实现日期和时间的高效格式化和解析,提高程序的性能和可读性。

    SimpleDateFormat使用详解

    SimpleDateFormat 使用详解 SimpleDateFormat 是 Java 中的一个日期和时间格式化类,它继承自 DateFormat 类。SimpleDateFormat 允许用户以各种方式格式化日期和时间,例如以年、月、日、时、分、秒等不同的格式来...

    simpleDateFormat是线程不安全的

    2. **缓存问题**:`SimpleDateFormat`在内部使用了缓存来提高性能,但这个缓存也是线程不安全的。在并发情况下,两个线程同时尝试修改或获取缓存的值,可能导致数据混乱。 3. **解析和格式化操作**:这两个操作不是...

    有关SimpleDateFormat的常用方法说明

    ### SimpleDateFormat的常用方法说明 #### 一、简介 `SimpleDateFormat`是Java中用于格式化日期和时间的一个类。它允许我们自定义日期/时间的显示格式,这在实际开发中非常有用,尤其是在处理不同地区或语言环境下...

    java SimpleDateFormat &Calendar

    在Java编程语言中,`SimpleDateFormat`和`Calendar`是两个重要的日期和时间处理类,它们在处理日期格式化、解析以及日期计算方面扮演着重要角色。本文将深入探讨这两个类的功能、用法以及它们之间的关系。 `...

    java SimpleDateFormat 显示于系统时间不符

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); date.setTime(time); System.out.println(sdf.format(date)); 发现时间于想要的时间不符,请运行Time.reg文件

    深入理解Java:SimpleDateFormat安全的时间格式化

    "深入理解Java:SimpleDateFormat安全的时间格式化" 在Java中,SimpleDateFormat是一个非常常用的类,用来对日期字符串进行解析和格式化输出。但是,如果使用不小心会导致非常微妙和难以调试的问题,因为DateFormat...

    java 使用SimpleDateFormat类获取系统的当前时间

    在Java编程语言中,`SimpleDateFormat` 是一个非常重要的日期和时间格式化工具类,它允许程序员以特定的模式来解析和格式化日期。当我们需要从系统获取当前时间并按照自定义格式显示时,`SimpleDateFormat` 就派上了...

    SimpleDateFormat格式化日期

    日期操作。。。基础的SimpleDateFormat格式化日期!!操作!》初级学习代码

    ThreadLocal:如何优雅的解决SimpleDateFormat多线程安全问题

    目录SimpleDateFormat诡异bug复现SimpleDateFormat诡异bug字符串日期转Date日期(parse)Date日期转String类型(format)SimpleDateFormat出现bug的原因如何解决SimpleDateFormat多线程安全问题局部变量使用...

    SimpleDateFormat线程不安全的5种解决方案.docx

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss"); // 创建时间对象 Date date = new Date(finalI * 1000); // 执行时间格式化并打印结果 System.out.println(simpleDateFormat.format(date...

    创建SimpleDateFormat对象,确定日期被格式化的格式.txt

    1.创建SimpleDateFormat对象,确定日期被格式化的格式 2.使用循环,在循环中调用Thread的sleep方法,让线程休眠1s后打印当前时间的字符串

    日期操作类(DateFormat、SimpleDateFormat)

    NULL 博文链接:https://chaoyi.iteye.com/blog/2082317

    Java多线程环境下SimpleDateFormat类安全转换

    "Java多线程环境下SimpleDateFormat类安全转换" 在Java多线程环境下,SimpleDateFormat类的使用可能会出现线程安全问题。本文主要介绍了Java多线程环境下SimpleDateFormat类的安全转换,通过示例代码详细介绍了如何...

    关于SimpleDateFormat的非线程安全问题及其解决方案.docx

    这种方法虽然能够解决线程安全问题,但是会降低程序的并发性能。 2. **使用ThreadLocal**:对于每个线程创建独立的`SimpleDateFormat`实例,可以通过`ThreadLocal`实现。这样,每个线程都有自己的`SimpleDateFormat...

    Java 实例 - 格式化时间SimpleDateFormat使用源代码-详细教程.zip

    Java 8引入了`java.time`包,其中的`DateTimeFormatter`类提供了更现代且性能更好的日期时间格式化功能。它是线程安全的,并且提供了更多的日期时间格式选项。 在学习过程中,你可以通过提供的源代码实例深入理解...

    Java中的SimpleDateFormat使用详解

    在性能要求较高的场景下,可以考虑使用`java.time`包中的`DateTimeFormatter`类,它是Java 8及更高版本引入的,提供了更高效且更易用的日期时间格式化功能。 总结来说,`SimpleDateFormat`是Java中处理日期和时间...

Global site tag (gtag.js) - Google Analytics