zoukankan      html  css  js  c++  java
  • 为什么SimpleDateFormat线程不安全?

    为什么SimpleDateFormat线程不安全?

    SimpleDateFormat中有个属性calendar(protected Calendar calendar;)继承自DataFormat, 所以当不同的线程如果用的是同一个SimpleDateFormat对象,则calendar对象也是完全相同的内存;

    SimpleDateFormat的format方法中将传入的时间date set给了calendar属性(calendar.setTime(date););

    当A线程传入时间a调用format方法进行格式化,format方法还没执行完,此时B线程传入时间b调用format方法进行格式化,则A线程中的calendar的time就变成了B线程传入的时间b了,当执行format方法中的subFormat方法时,获取calendar中的时间就线程不安全了。

    java.text.SimpleDateFormat#format(java.util.Date, java.lang.StringBuffer, java.text.Format.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; ) {
            // 省略部分代码……
            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;
    }

    java.text.SimpleDateFormat#subFormat 源码:

    private void subFormat(int patternCharIndex, int count,
                           FieldDelegate delegate, StringBuffer buffer,
                           boolean useDateFormatSymbols)
    {
        int     maxIntCount = Integer.MAX_VALUE;
        String  current = null;
        int     beginOffset = buffer.length();
    
        int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
        int value;
        if (field == CalendarBuilder.WEEK_YEAR) {
            if (calendar.isWeekDateSupported()) {
                value = calendar.getWeekYear();
            } else {
                // use calendar year 'y' instead
                patternCharIndex = PATTERN_YEAR;
                field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
                value = calendar.get(field);
            }
        } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) {
            value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
        }
          // 省略部分代码…… 

    解决方法

    1. 不同线程使用不同的SimpleDateFormat对象,每次new一个新的出来或者使用ThreadLocal,缺点消耗内存资源
    2. 要执行SimpleDateFormat之前进行加锁,缺点效率不高
    3. 使用其他时间类比如Java8线程安全DateTimeFormatter,缺点可能是要重构老代码等
  • 相关阅读:
    PHP的Tag标签
    PHP 常量
    MySQL知识点链接
    Codeforces Round #593 (Div. 2)D(螺旋形模拟)
    【PAT甲级】1060 Are They Equal (25 分)(需注意细节的模拟)
    【PAT甲级】1059 Prime Factors (25 分)
    【PAT甲级】1058 A+B in Hogwarts (20 分)
    【PAT甲级】1057 Stack (30 分)(分块)
    【PAT甲级】1056 Mice and Rice (25 分)
    Atcoder Grand Contest 039C(容斥原理,计数DP)
  • 原文地址:https://www.cnblogs.com/theRhyme/p/12395553.html
Copyright © 2011-2022 走看看