SimpleDateFormat一定是線程不安全嗎?


今天一位優秀的架構師告訴我,下面這段代碼SimpleDateFormat是線程不安全的。

    /**
     * 將Date按格式轉化成String
     *
     * @param date  Date對象
     * @param pattern 日期類型
     * @return String
     */
    public static String date2String(Date date, String pattern) {
        if (date == null || pattern == null) {
            return null;
        }
        return new SimpleDateFormat(pattern).format(date);
    }

 

 

那么let us test!

簡單介紹下我的測試方法

1.時間轉字符串

2.字符串轉時間

3.時間轉字符串

比較第一個字符串和第二個字符是否相同。如果沒有並發問題,那么第一個字符串跟第二個字符串肯定完全一樣

第一種情況

一個SimpleDateFormat實例,並發執行。

private static void newSimpleDate() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(1000));
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        if (!dateString.equals(dateString2)) {
                            System.out.println(dateString.equals(dateString2));
                        }
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

 

結果:

false
false
false
false
false
false
false

 

說明存在線程不安全的問題。

 

第二種情況

每次新建一個SimpleDateFormat 對象

    private static void oneSimpleDate() {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(1000));
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        date2String(new Date(), "yyyy-MM-dd HH:mm:ss");
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
    public static void date2String(Date date, String pattern) throws ParseException {

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);

        String dateString = new SimpleDateFormat(pattern).format(date);
        Date parseDate = simpleDateFormat.parse(dateString);
        String dateString2 = simpleDateFormat.format(parseDate);
     System.out.println(dateString.equals(dateString2));
    }

 

結果:永遠為true

true
true
true
true
true
true
true

 

說明沒有線程安全問題。

 

奇怪,那么SimpleDateFormat究竟有沒有問題呢?

簡單看一下SimpleDateFormat.format()方法。

 
         
protected Calendar calendar;

public
StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) { pos.beginIndex = pos.endIndex = 0; return format(date, toAppendTo, pos.getFieldDelegate()); } // Called from Format after creating a FieldDelegate private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i < compiledPattern.length; ) { int tag = compiledPattern[i] >>> 8; int count = compiledPattern[i++] & 0xff; if (count == 255) { count = compiledPattern[i++] << 16; count |= compiledPattern[i++]; } switch (tag) { case TAG_QUOTE_ASCII_CHAR: toAppendTo.append((char)count); break; case TAG_QUOTE_CHARS: toAppendTo.append(compiledPattern, i, count); i += count; break; default: subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols); break; } } return toAppendTo; }

可以看到,多個線程之間共享變量calendar,並修改calendar。因此在多線程環境下,當多個線程同時使用相同的SimpleDateFormat對象(如static修飾)的話,如調用format方法時,多個線程會同時調用calender.setTime方法,導致time被別的線程修改,因此線程是不安全的。此外,parse方法也是線程不安全的。

 

SimpleDateFormat不是線程安全的,但這並不代表,它無法被線程安全的使用,當你把它作為局部變量,每次新建一個實例,或者加鎖,或者采用架構師說的DateTimeFormatter都能規避這個問題。

就像hashmap,大家都知道他是線程不安全的,在jdk1.7采用頭插法時,並發會出現死循環。但是你每次new hashmap對象,去put肯定不會有問題,盡管不會有人這么用,業務也不允許。

 

所以,這件事告訴我們,不要守着自己的教條主義,看到線程不安全的類,就覺得方法是線程不安全的,要看具體的使用場景;當然線程安全的類,也有可能是線程不安全的;架構師也不例外喔!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM