SimpleDateFormat的線程安全問題
在多個線程併發的情況下,同時需要用到SimpleDateFormat的情況下會出現很多問題,會發生線程掛掉,日期轉行異常等
原因
在JDK的API中有關於SimpleDateFormat的說明:Synchronization
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.SimpleDateFormat不能進行線程同步,也就是不具備線程安全,官方建議在多線程情況下,不要單獨的使用SimpleDateFormat,如果必須要債多線程情況下使用,一定要進行同步synchronized;
分析
這也難怪,往往只顧及SimpleDateFormat的好用,但卻沒注意這些東西,會犯一些小錯誤。警戒
源碼解析
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);//我們傳入的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;
}
public abstract class DateFormat extends Format {
/**
* The {@link Calendar} instance used for calculating the date-time fields
* and the instant of time. This field is used for both formatting and
* parsing.
*
* <p>Subclasses should initialize this field to a {@link Calendar}
* appropriate for the {@link Locale} associated with this
* <code>DateFormat</code>.
* @serial
*/
protected Calendar calendar;
因爲在SimpleDateFormat的父類DateFormat中,protected Calendar calendar;Calender類是關於時間和時區等的類,請注意它是一個成員變量,也就是說多個線程情況下,第一線程起來會改變它,還沒結束是第二個線程也起來了,在第一個線程結束之前卻也改變他了,有可能第一個線程因爲第二個線程的改變而去執行第二個線程的結果。這就離我們預期的結果大大不符合,故而發生線程掛掉,時間不對等錯誤。所以官方鼓勵我們進行synchronized。
這也就是爲什麼SimpleDateFormat在多個線程試用下會發生同步問題
解決方案
在每個調用SimpleDateFormat的時,都new一個實例,也不會發生線程問題,但是會增加內存的開銷,相對共享一個實例來說,只會浪費性能
解決方案Demo
/**
*
* 該類爲多線程環境下的SimpleDateFormat工具類
* 解決多線程的併發問題
* SimpleDateFormat 的candler類 爲多個線程公用,所以要線程同步
* @author shr
*
*/
public class ThreadSimpleDateFornat {
public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 解析Dtae,按照設置的時間日期格式返回字符串
* @param date
* @return
*/
public String format(Date date){
synchronized(dateFormat){
String format = dateFormat.format(date);
return format;
}
}
/**
* 解析包含時間的字符串,並且返回一個已經格式化的Date
* @param dateString
* @return
* @throws ParseException
*/
public Date parse(String dateString) throws ParseException{
synchronized(dateFormat){
return dateFormat.parse(dateString);
}
}
}
解決方案Demo2
思路:需要在乎那一點性能提升的,可以使用ThreadLocal作爲容器,儲存SipmleDateFormat
進行提升性能,但是那一點性能又有誰在乎呢