FindBugs分析記錄][H STCAL]Call to static DateFormat

今天小弟接到要修改findBugs的活計,有些新的體會,共享一下java

Bug: Call to method of static java.text.DateFormat
Pattern id: STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE, type: STCAL, category: MT_CORRECTNESS安全

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.多線程

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.網站

 

    上面的英文解釋其實應該說得比較清楚,在Java文檔中,已經明確說明了DateFormats 是非線程安全的,而在SimpleDateFormat的Jdk 的Source文件中,咱們也找到這麼一段註釋,說明它不是線程安全的。this

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均可以印證這個問題。orm

    致使SimpleDateFormat出現多線程安全問題的緣由,是由於:SimpleDateFormat處理複雜,Jdk的實現中使用了成員變量來傳遞參數,這就形成在多線程的時候會出現錯誤。對象

    而Findbugs所說的「Call to static DateFormat」,其實就是一些人:爲了漸少new 的次數而把SimpleDateFormat作成成員或者靜態成員,上面已經說了,這樣作是不安全的。ip

    其實,出現這種問題的代碼通常都長得差很少,典型的代碼示例以下:文檔

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());
     }
 )

 

 

再給個詳細例子說明問題,看下面代碼:

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對象便可。

相關文章
相關標籤/搜索