在一個讀取數據庫數據導出到excel文件的例子當中,每次處理一個時間信息的時候,就需要創建一個SimpleDateFormat實例對象,然后再丟棄這個對象。大量的對象就這樣被創建出來,占用大量的內存和 jvm空間。
三種解決方案:
1.需要的時候創建新實例:
package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { public static String formatDate(Date date)throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static Date parse(String strDate) throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.parse(strDate); } }
說明:在需要用到SimpleDateFormat 的地方新建一個實例,不管什么時候,將有線程安全問題的對象由共享變為局部私有都能避免多線程問題,不過也加重了創建對象的負擔。在一般情況下,這樣其實對性能影響比不是很明顯的。
2.使用同步:同步SimpleDateFormat對象
package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateSyncUtil { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date)throws ParseException{ synchronized(sdf){ return sdf.format(date); } } public static Date parse(String strDate) throws ParseException{ synchronized(sdf){ return sdf.parse(strDate); } } }
說明:當線程較多時,當一個線程調用該方法時,其他想要調用此方法的線程就要block,多線程並發量大的時候會對性能有一定的影響。
3.使用ThreadLocal:
package com.peidasoft.dateformat; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ConcurrentDateUtil { private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() { @Override protected DateFormat 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); } }
另外一種寫法:
package com.peidasoft.dateformat; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ThreadLocalDateUtil { private static final String date_format = "yyyy-MM-dd HH:mm:ss"; private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
public static DateFormat getDateFormat() { DateFormat df = threadLocal.get(); if(df==null){ df = new SimpleDateFormat(date_format); threadLocal.set(df); } return df; } public static String formatDate(Date date) throws ParseException { return getDateFormat().format(date); } public static Date parse(String strDate) throws ParseException { return getDateFormat().parse(strDate); } }
說明:使用ThreadLocal, 也是將共享變量變為獨享,線程獨享肯定能比方法獨享在並發環境中能減少不少創建對象的開銷。如果對性能要求比較高的情況下,一般推薦使用這種方法。
4.拋棄JDK,使用其他類庫中的時間格式化類:
1.使用Apache commons 里的FastDateFormat,宣稱是既快又線程安全的SimpleDateFormat, 可惜它只能對日期進行format, 不能對日期串進行解析。
2.使用Joda-Time類庫來處理時間相關問題
做一個簡單的壓力測試,方法一最慢,方法三最快,但是就算是最慢的方法一性能也不差,一般系統方法一和方法二就可以滿足,所以說在這個點很難成為你系統的瓶頸所在。從簡單的角度來說,建議使用方法一或者方法二,如果在必要的時候,追求那么一點性能提升的話,可以考慮用方法三,用ThreadLocal做緩存。