問題
我們寫了個DateUtil,內部有個SimpleDateFormat,是個static,我們想全局公用此常量
public class DateUtil {
private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms";
private static final SimpleDateFormat format=new SimpleDateFormat(MESSAGE_FORMAT, Locale.getDefault());
public static final DateFormat getDateFormat() {
return format;
}
}
如果要在多線程的環境下使用DateUtil,是會出問題的,SimpleDateFormat的parse方法是存在線程安全問題的,如果直接這麼使用,很可能會在SimpleDateFormat的parse方法內部崩潰
方法0:同步
把parse也封到工具類內,且對parse加同步鎖
public class DateUtil0 {
private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms";
private static final SimpleDateFormat format=new SimpleDateFormat(MESSAGE_FORMAT, Locale.getDefault());
public static final DateFormat getDateFormat() {
return format;
}
public Date parse(String str) {
try {
synchronized(format){
return format.parse(str);
}
} catch (ParseException e) {
}
return null;
}
}
這樣沒問題,但是加了鎖,效率嚴重下降。
public class DateUtil1 {
private static final ThreadLocal<DateFormat> messageFormat = new ThreadLocal<DateFormat>();
private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms";
private static final DateFormat getDateFormat() {
DateFormat format = messageFormat.get();
if (format == null) {
format = new SimpleDateFormat(MESSAGE_FORMAT, Locale.getDefault());
messageFormat.set(format);
}
return format;
}
}
hreadLocal是專門爲解決此類問題而存在的,當我們不想每次使用都new一個東西,就會使用static,一次創建,到處都能用,但是使用的時候往往又有線程安全問題。其實只要不同線程裏有一個不同的實例,就解決了這個問題。對於上例,每個線程創建一個SimpleDateFormat,互相獨立。線程內公用同一個,不同線程之間相互隔離.SimpleDateFormat。ThreadLocal就是根據上述思想被創建出來的。
核心思想在getDateFormat內部,首先去get,如果當前線程已有對應實例,就直接返回,如果沒有,就創建一個並返回。
方法2:ThreadLocal的匿名內部類寫法
public class DateUtil2 {
private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms";
private static final ThreadLocal messageFormat = new ThreadLocal(){
protected synchronized Object initialValue() {
return new SimpleDateFormat(MESSAGE_FORMAT, Locale.getDefault());
}
};
private static final DateFormat getDateFormat() {
return (DateFormat) messageFormat.get();
}
}
getDateFormat會去調get方法,get方法內部檢查當前線程是否有實例存在,有就直接返回,無就調用initialValue來new一個,注意這裏initialValue要加同步鎖
方法3 匿名內部類返回null的寫法
1,2的雜交,將無實例就new寫在getDateFormat裏
public class DateUtil3 {
private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms";
private static final ThreadLocal messageFormat = new ThreadLocal(){
protected synchronized Object initialValue() {
return null;
}
};
private static final DateFormat getDateFormat() {
DateFormat df = (DateFormat) messageFormat.get();
if (df == null) {
df = new SimpleDateFormat(MESSAGE_FORMAT, Locale.getDefault());
messageFormat.set(df);
}
return df;
}
}
方法4,3的簡化
public class DateUtil4 {
private static final String MESSAGE_FORMAT = "MM-dd HH:mm:ss.ms";
private static final ThreadLocal messageFormat = new ThreadLocal();
private static final DateFormat getDateFormat() {
DateFormat df = (DateFormat) messageFormat.get();
if (df == null) {
df = new SimpleDateFormat(MESSAGE_FORMAT, Locale.getDefault());
messageFormat.set(df);
}
return df;
}
}
4和1比較,其實差別就在於一個使用泛型,一個使用強轉,最優方案還是1
覺得不錯請點贊支持,歡迎留言或進我的個人羣855801563領取【架構資料專題目合集90期】、【BATJTMD大廠JAVA面試真題1000+】,本羣專用於學習交流技術、分享面試機會,拒絕廣告,我也會在羣內不定期答題、探討