轉:使用SimpleDateFormat必須注意的問題

在使用SimpleDateFormat的經常會有一些錯誤的用法,例如如下方式:

public class TestDateFormat{
     
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
     
public void method1(){
         sdf.format(
new Date());
     }

     
public void method2(){
         sdf.format(
new Date());
     }

 )

爲了漸少new 的次數而把SimpleDateFormat做成成員或者靜態成員,但這樣的做法是隱含着錯誤的,是不安全
 的。如下給出證明:

 import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


public class TestDateFormat{
    
private SimpleDateFormat sdf ;
    
public static void main(String[] args) {
        SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
        Date date1 
= new Date();
        Date date2 
= new Date(date1.getTime()+1000*60*60*24);
        System.out.println(date1);
        System.out.println(date2);
        Thread thread1 
= new Thread(new Thread1(sdf,date1));
        thread1.start();
        Thread thread2 
= new Thread(new Thread2(sdf,date2));
        thread2.start();
    }

    
}

class Thread1 implements Runnable{
    
private SimpleDateFormat sdf;
    
private Date date;
    
public Thread1(SimpleDateFormat sdf,Date date){
        
this.sdf = sdf;
        
this.date = date;
    }

    
public void run() {
        
for(;;){
            String strDate 
= sdf.format(date);
            
if("2007-02-27".equals(strDate)){
                System.err.println(
"Error: date1="+strDate);
                System.exit(
0);
            }

        }

    }

}

class Thread2 implements Runnable{
    
private SimpleDateFormat sdf;
    
private Date date;
    
public Thread2(SimpleDateFormat sdf,Date date){
        
this.sdf = sdf;
        
this.date = date;
    }

    
public void run() {
        
for(;;){
            String strDate 
= sdf.format(date);
            
if("2007-02-26".equals(strDate)){
                System.err.println(
"Error: date1="+strDate);
                System.exit(
0);
            }

        }

    }

}

很快,基本幾十次就會出現錯誤。錯誤原因:
 因爲SimpleDateFormat處理複雜,Jdk的實現中使用了成員變量來傳遞參數,這個就造成在多線程的時候
 會出現錯誤。上邊的程序證明了這個問題。

再看看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中
 Bug ID:  4228335  講的就是這個問題。
 SimpleDateFormat is not threadsafe (one more try)
 其中有這麼幾段話值得關注:
 1,
 Aside from the obvious, there are two reasons why this fix should be made:
- The documentation for DateFormat states that a DateFormat object should be used
multiple times, implying that construction is expensive.  Furthermore,
 no mention is made of SimpleDateFormat's lack of thread-safety. 
 Since for most applications the date formats remain constant,
 it is very easy to conclude that DateFormats should be application globals.
 But SimpleDateFormat produces incorrect results when used in this way.
- Bug 4101500, a similar problem with NumberFormat, was fixed.

建議修改這個問題,而且NumberFormat已經修改,解決了這個問題。簡單測試了一下NumberFormat確是不出錯

2,
Use of a cloned Calendar object in format(), as suggested in JDC comments,
slows down the execution 30-50%. And it potentially affects footprint because
of cloned objects. Another approach would be to have a Calendar instance per
thread. However, this approach will cause problems at the API semantic level due
to the get/setCalendar methods.
這一段解釋了爲什麼沒修改這個問題,一個是保持API不變,另一個是因爲Clone比new慢,會損失效率。

結論:每次使用它的時候現new,或者用一個同步函數把new好的包裝起來使用吧。

 

發佈了45 篇原創文章 · 獲贊 0 · 訪問量 1465
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章