一 常用方案:SimpleDateFormat
public class TimeUtils {
public static String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static Date parse(String strDate) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return sdf.parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
} |
二 出現的問題
每次調用方都要new SimpleDateFormat(),每次處理一個時間信息的時候,就需要創建一個SimpleDateFormat實例對象,然后再丟棄這個對象。大量的對象就這樣被創建出來,占用大量的內存和 jvm空間
於是 那我就創建一個靜態的simpleDateFormat實例
public class TimeUtils {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) {
return sdf.format(date);
}
public static Date parse(String strDate) {
try {
return sdf.parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
} |
但是問題是:線程不安全,在format方法里,有這樣一段代碼:
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
} |
calendar不是方法局部變量而是SimpleDateFormat類的全局變量,而這就是引發問題的根源。想象一下,在一個多線程環境下,有兩個線程持有了同一個SimpleDateFormat的實例,分別調用format方法:
線程1調用format方法,改變了calendar這個字段。
中斷來了。
線程2開始執行,它也改變了calendar。
又中斷了。
線程1回來了,此時,calendar已然不是它所設的值,而是走上了線程2設計的道路。如果多個線程同時爭搶calendar對象,則會出現各種問題,時間不對,線程掛死等等。
分析一下format的實現,我們不難發現,用到成員變量calendar,唯一的好處,就是在調用subFormat時,少了一個參數,卻帶來了這許多的問題。其實,只要在這里用一個局部變量,一路傳遞下去,所有問題都將迎刃而解。
三 解決辦法:
1 SimpleDateFormat作為線程局部變量來使用。2使用對象鎖,代碼如下。3使用jodaTime(推薦)
public class TimeUtils {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) {
synchronized(sdf){
return sdf.format(date);
}
}
public static Date parse(String strDate) {
synchronized(sdf){
try {
return sdf.parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
} |
四 jodaTime使用:
public class TimeUtils {
public static String formatDate(Date date) {
DateTime dateTime = new DateTime(date);
String formatStr = "yyyy-MM-dd HH:mm:ss";
return dateTime.toString(formatStr);
}
public static Date parse(String strDate) {
DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dateTime = DateTime.parse(strDate, dateTimeFormat);
dateTime = dateTime.plusDays(1);
return dateTime.toDate();
}
} |