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,缺点可能是要重构老代码等
  • 相关阅读:
    [页面布局方式]
    padding and margin
    【浏览器中的页面】
    【浏览器的页面循环系统】
    Activity启动模式详解(二)--->singleTask
    finish、onDestory、System.exit的区别
    Androidndk开发打包时我们应该如何注意平台的兼容(x86,arm,arm-v7a)
    关于WifiManager的一些看法
    高效的找出两个List中的不同元素
    关于Activity的生命周期
  • 原文地址:https://www.cnblogs.com/theRhyme/p/12395553.html
Copyright © 2011-2022 走看看