Bug: Call to method of static Java.text.DateFormat
Pattern id: STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE, type: STCAL, category: MT_CORRECTNESShtml
As the JavaDoc states, DateFormats are inherently unsafe for multithreaded use. The detector has found a call to an instance of DateFormat that has been obtained via a static field. This looks suspicous.java
For more information on this see Sun Bug #6231579 and Sun Bug #6178997.apache
上面的英文解釋其實應該說得比較清楚,在Java文檔中,已經明確說明了DateFormats 是非線程安全的,而在SimpleDateFormat的Jdk 的Source文件中,咱們也找到這麼一段註釋,說明它不是線程安全的。安全
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多線程
在Sun本身的網站上。在sun的bug database中,Sun Bug #6231579 ,Sun Bug #6178997均可以印證這個問題。網站
致使SimpleDateFormat出現多線程安全問題的緣由,是由於:SimpleDateFormat處理複雜,Jdk的實現中使用了成員變量來傳遞參數,這就形成在多線程的時候會出現錯誤。this
而Findbugs所說的「Call to static DateFormat」,其實就是一些人:爲了漸少new 的次數而把SimpleDateFormat作成成員或者靜態成員,上面已經說了,這樣作是不安全的。.net
其實,出現這種問題的代碼通常都長得差很少,典型的代碼示例以下:線程
public class Test { private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); public void method1() { dateFormat.format(new Date()); } public void method2() { dateFormat.format(new Date()); } }
再給個詳細例子說明問題,看下面代碼:code
import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; public class Test { private SimpleDateFormat dateFormat; public static void main(String[] args) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date today = new Date(); Date tomorrow = new Date(today.getTime() + 1000 * 60 * 60 * 24); System.out.println(today); // 今天是2010-01-11 System.out.println(tomorrow); // 明天是2010-01-11 Thread thread1 = new Thread(new Thread1(dateFormat, today)); thread1.start(); Thread thread2 = new Thread(new Thread2(dateFormat, tomorrow)); thread2.start(); } } class Thread1 implements Runnable { private SimpleDateFormat dateFormat; private Date date; public Thread1(SimpleDateFormat dateFormat, Date date) { this.dateFormat = dateFormat; this.date = date; } public void run() { for (;;) {// 一直循環到出問題爲止吧。 String strDate = dateFormat.format(date); // 若是不等於2010-01-11,證實出現線程安全問題了!!!! if (!"2010-01-11".equals(strDate)) { System.err.println("today=" + strDate); System.exit(0); } } } } class Thread2 implements Runnable { private SimpleDateFormat dateFormat; private Date date; public Thread2(SimpleDateFormat dateFormat, Date date) { this.dateFormat = dateFormat; this.date = date; } public void run() { for (;;) { String strDate = dateFormat.format(date); if (!"2010-01-12".equals(strDate)) { System.err.println("tomorrow=" + strDate); System.exit(0); } } } }
運行的結果以下:
Mon Jan 11 11:30:36 CST 2010 Tue Jan 12 11:30:36 CST 2010 tomorrow=2010-01-11
終於看到問題了,tomorrow=2010-01-11,錯得很明顯了。其實要避免這個問題方法很簡單,不使用SimpleDateFormat,或者不使用成員變量/靜態成員變量的SimpleDateFormat對象便可。
以上出自: http://www.cnblogs.com/hyddd/articles/1643978.html
解決辦法:使用org.apache.commons.lang.time.DateFormatUtils,示例:
private final static String YYYY = "yyyy"; private final static String sdfDay = "yyyy-MM-dd"; private final static String sdfDays = "yyyyMMdd"; private final static String sdfTime = "yyyy-MM-dd HH:mm:ss"; /** * 獲取YYYY格式 * * @return */ public static String getYear() { return DateFormatUtils.format(new Date(), YYYY); } /** * 獲取YYYY-MM-DD格式 * * @return */ public static synchronized String getDay() { return DateFormatUtils.format(new Date(), sdfDay); } /** * 獲取YYYYMMDD格式 * * @return */ public static synchronized String getDays() { return DateFormatUtils.format(new Date(), sdfDays); } /** * 獲取YYYY-MM-DD HH:mm:ss格式 * * @return */ public static synchronized String getTime() { return DateFormatUtils.format(new Date(), sdfTime); }