1. 程式人生 > 其它 >在專案開發中,元件庫設計一定要遵循一定原則

在專案開發中,元件庫設計一定要遵循一定原則

在java8之前,我們對時間的格式化處理,一般都是用的SimpleDateFormat類實現的。例如:

@Service
public class SimpleDateFormatService {

    public Date time(String time) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFormat.parse(time);
    }
}

如果你真的這樣寫,是沒問題的。

就怕哪天抽風,你覺得dateFormat是一段固定的程式碼,應該要把它抽取成常量。

於是把程式碼改成下面的這樣:

@Service
public class SimpleDateFormatService {

   private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public Date time(String time) throws ParseException {
        return dateFormat.parse(time);
    }
}

dateFormat物件被定義成了靜態常量,這樣就能被所有物件共用。

如果只有一個執行緒呼叫time方法,也不會出現問題。

但Serivce類的方法,往往是被Controller類呼叫的,而Controller類的介面方法,則會被tomcat執行緒池呼叫。換句話說,可能會出現多個執行緒呼叫同一個Controller類的同一個方法,也就是會出現多個執行緒會同時呼叫time方法的情況。

而time方法會呼叫SimpleDateFormat類的parse方法:

@Override
public Date parse(String text, ParsePosition pos) {
    ...
    Date parsedDate;
    try {
        parsedDate = calb.establish(calendar).getTime();
        ...
    } catch (IllegalArgumentException e) {
        pos.errorIndex = start;
        pos.index = oldStart;
        return null;
    }
   return parsedDate;

該方法會呼叫establish方法:

Calendar establish(Calendar cal) {
    ...
    //1.清空資料
    cal.clear();
    //2.設定時間
    cal.set(...);
    //3.返回
    return cal;
}

其中的步驟1、2、3是非原子操作。

但如果cal物件是區域性變數還好,壞就壞在parse方法呼叫establish方法時,傳入的calendar是SimpleDateFormat類的父類DateFormat的成員變數:

public abstract class DateFormat extends Forma {
    ....
    protected Calendar calendar;
    ...
}

這樣就可能會出現多個執行緒,同時修改同一個物件即:dateFormat,他的同一個成員變數即:Calendar值的情況。

這樣可能會出現,某個執行緒設定好了時間,又被其他的執行緒修改了,從而出現時間錯誤的情況。

那麼,如何解決這個問題呢?

  1. SimpleDateFormat類的物件不要定義成靜態的,可以改成方法的區域性變數。
  2. 使用ThreadLocal儲存SimpleDateFormat類的資料。
  3. 使用java8的DateTimeFormatter類。

來自:

https://mp.weixin.qq.com/s/GCJaMeKiqyHd8okIP5Y_sQ