以下为转http://www.thedwick.com/blog/2008/04/simpledateformat-performance-pig/的一篇关于simpleDateformat中使用的一些误区以及怎么样来安全有效地使用simpleDateFormat。在本文中,描述了创造simpleDateFormat的昂贵代价,以及使用静态实例的多线程错误以及同步时的低效问题,并提出一个提升的解决办法。
全文如下:
Just yesterday I came across this problem “in the wild” for the third time in my career so far: an application with performance problems creating tons of java.text.SimpleDateFormat instances. So, I have to get this out there: creating a new instance of SimpleDateFormat is incredibly expensive and should be minimized. In the case that prompted this post, I was using JProfiler to profile this code that parses a CSV file and discovered that 50% of the time it took to suck in the file and make 55,000 objects out of it was spent solely in the constructor of SimpleDateFormat. It created and then threw away a new one every time it had to parse a date. Whew!
“Great,” you think, “I’ll just create one, static instance, slap it in a field in a DateUtils helper class and life will be good.”
Well, more precisely, life will be good about 97% of the time. A few days after you roll that code into production you’ll discover the second cool fact that’s good to know: SimpleDateFormat is not thread safe. Your code will work just fine most of the time and all of your regression tests will probably pass, but once your system gets under a production load you’ll see the occasional exception.
“Fine,” you think, “I’ll just slap a ‘synchronized’ around my use of that one, static instance.”
Ok, fine, you could do that and you’d be more or less ok, but the problem is that you’ve now taken a very common operation (date formatting and parsing) and crammed all of your otherwise-lovely, super-parallel application through a single pipe to get it done.
What would be better is to use a ThreadLocal variable so you can have your cake and eat it, too:
public class DateUtils { public static final String MY_STANDARD_DATE_FORMAT = "yyyyMMdd"; public static java.util.Date parseDate(String dateString) throws ParseException { return getFormat().parse(dateString); } private static ThreadLocal format = new ThreadLocal(){ protected synchronized Object initialValue() { return new java.text.SimpleDateFormat(MY_STANDARD_DATE_FORMAT); } } private static DateFormat getFormat(){ return (DateFormat) format.get(); } }
I hope this code works because I wrote it on the fly and haven’t tried to run it, but you get the point.
以下为翻译:
昨天我碰到一个关于创建simpleDateFormat所带来的性能问题,并得出一个以下的结论:你应当尽量少的创建simpleDateFormat实例,因为创建这么一个实例需要耗费很大的代价。在一个解析csv文件的例子当中,当我使用jprofiler对这个例子进行性能分析时,发现它在创建在创建simpleDateFormat上就花费了55 000个对象,每次处理时间信息时,就创建一个simpleDateFormat,然后再丢弃这个对象。大量的对象就这样被创建出来,占用大量的内存和jvm空间。
你也许会说,OK,那我就创建一个静态的simpleDateFormat实例,然后放到一个DateHelper类中,在使用时直接使用这个实例进行操作,这样问题就解决了。
当然,这个方法的确很不错,在97%的时间里面都会工作得很好。但当你在生产环境中使用一段时间之后,你就会发现这么一个事实:它不是线程安全的。在正常的测试情况之下,都没有问题,但一旦在生产环境中一定负载情况下时,这个问题就出来了。
那么,也可以在这个format方法上增加一个synchronized方法,使之同步运行,这样也解决了线程安全的问题了。
这个解决方法没有问题,的确可以这样做。但是你这样做的后果在于,将一个经常会使用的格式化和解析时间的方法由原来的串行运行变成了单个运行,效率可想而知。
有更好的方法来代替这个同步化的一个例子就是使用线程本地变量,将simpleDateFormat放到线程变量中来运行,避免了在多个线程中共享simpleDateFormat的问题,同时在一个线程中又可以重用同一个simpleDateFormat。
在原文中,有一个回复说明了为什么创建一个simpleDateFormat会花费很大的代价了,原评论如下所示:
note that performance is bad because SimpleDateFormat has a calendar instance as a member and it is allocation of the calendar object that is expensive. This is also why it isn’t thread safe – if 2 threads change the calendar da te or timezone or something at once the formats returned will be inconsistent.
上面的解释在于simpleDateFormat有一个calender的实例变量,分配这个变量的代价是昂贵的。这也是为什么它不是线程安全的原因了,如果2个线程同时修改了这个calendar的时间信息,那么format的输出自然是不正确的。有如下代码所证(来自于simpleDateFormat的format相关方法):
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date);
转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201108210002.html