記一次SimpleDateFormat出現的線程安全問題

    以前只把SimpleDateFormat類當前一個簡單的工具類使用,並沒有注意它存在的線程安全問題,直到最近在近期一個數據遷移項目中才碰到。我的遷移程序會比較遷移前和後的數據是否一致,在做這個事情的時候,由於之前的數據庫中存儲的日期使用的14位字符串,即20140502112230,而新庫中規範要求使用Timestamp類型,這自然要涉及到日期類型與字符串之間的轉化。因爲日期格式固定,因此我將這個方法封裝在了一個工具類中,並將SimpleDateFormat類的變量聲明成了一個類變量,如下:

public class Util {
    // 聲明瞭一個靜態變量
    static final SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss");

    public static Timestamp str2TimeStamp(String datestr) {
        try {
            if (StringUtils.isBlank(datestr) || datestr.length() != 14) {
                return null;
            }
            Date d = yyyyMMddHHmmss.parse(datestr);
            return new Timestamp(d.getTime());
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }

    public static String timeStamp2String(Timestamp t) {
        if (t == null) {
            return null;
        }
        return yyyyMMddHHmmss.format(t);
    }
}

   在執行多線程的比較程序過程中,發現日期值老是錯誤,斷點查看,明明字符串是20140502112230,Timestamp卻出現了另外的值,才意識到SimpleDateFormat的線程安全問題,趕緊查看javadoc:

Synchronization

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 externally.

   意思是說,該日期格式類不是同步的。所以建議在使用的時候,爲每個線程單獨創建一個SimpleDateForm實例。如果非要多個線程併發地訪問一個實例,那麼必須使用額外的同步。

    或者我們可以把相應的對象放到ThreadLocal中,多個線程調用時使用的都是各自線程中的副本,就不會出現線程安全問題了。

public static final ThreadLocal<SimpleDateFormat> sdfThread = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章