您的位置:首页 > 编程语言 > Java开发

【Java 陷阱】SimpleDateFormat 时间格式化多线程异常

2015-08-22 14:11 489 查看

SimpleDateFormat 陷阱

SimpleDateFormat是java中常用的时间格式化工具,除了转化时间格式,还可以解析字符串到时间类型,方便好用,但是陷阱就在其中。如果你遇到类似看到时间字符串完全正确,但就是不能正确格式化,或者明明时间类型没错,但就是无法转化为其他格式,亦或是格式转化错误随机出现,那想想,是不是陷入了多线程并发的陷阱。

调用:

private static SimpleDateFormat sdf = new SimpleDataFormat("yyyy-MM-dd HH:mm:ss");
...
...

public static Date parseDateFromString(String str){
try{
return sdf.parse(str);
}catch(Exception e){
//TODO
}
}
...
...


多线程情况下数据随机抛出异常:

java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082)
at java.lang.Double.parseDouble(Double.java:510)
at java.text.DigitList.getDouble(DigitList.java:151)
at java.text.DecimalFormat.parse(DecimalFormat.java:1302)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
at java.text.DateFormat.parse(DateFormat.java:335)


这种问题在单机情况下调试难以发现问题,根本原因在于SimpleDateFormat本身不是线程安全的:

SimpleDateFormat.java

*** Date formats are not synchronized.**
* It is recommended to create separate format instances for each thread.
* If multiple threads access a format concurrently, it must be synchronized
* externally.
*
* @see          <a href="http://java.sun.com/docs/books/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
* @see          java.util.Calendar
* @see          java.util.TimeZone
* @see          DateFormat
* @see          DateFormatSymbols
* @version      1.88, 05/28/08
* @author       Mark Davis, Chen-Lieh Huang, Alan Liu
*/
public class SimpleDateFormat extends DateFormat {
**
..
protected Calendar calendar;
..
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
.....
}
public Date parse(String text, ParsePosition pos)
{
int start = pos.index;
int oldStart = start;
int textLength = text.length();
calendar.clear(); // Clears all the time fields
...
...
}
}


从上面代码片段可以看到,多线程下,对象calendar会被频繁的修改,从而导致不可预知的错误。

解决方案:

SimpleDateFormat 实例化在方法内部,每次重新定义变量

不好,导致format对象频繁创建,大并发下影响性能

同步代码块,对sdf共享变量加锁 synchronize(sdf)

不好,并发下加锁会降低系统的并发性能,极限情况下退化为单线程,不推荐

使用ThreadLocal,隔离线程共享变量

推荐使用,类似如下:

private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}public static String format(Date date) {
return threadLocal.get().format(date);
}


结束
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息