• SimpleDateFormat线程不安全和DateTimeFormatter线程安全


    1.SimpleDateFormat线程不安全
    输出时间不安全

    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++];
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    public abstract class DateFormat extends Format {
        protected Calendar calendar;
    
    • 1
    • 2
    public class SimpleDateFormat extends DateFormat
    
    • 1

    可以看出calendar当多个线程访问的时候会存在不安全的情况,因为calendar数值可能被改变。

    输出格式线程不安全
    再看SimpleDateFormat的构造器

        public SimpleDateFormat(String pattern, Locale locale)
        {
            if (pattern == null || locale == null) {
                throw new NullPointerException();
            }
            initializeCalendar(locale);
            this.pattern = pattern;
            this.formatData = DateFormatSymbols.getInstanceRef(locale);
            this.locale = locale;
            initialize(locale);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    追踪pattern发现pattern数值可被修改

     public void applyLocalizedPattern(String pattern) {
             String p = translatePattern(pattern,
                                         formatData.getLocalPatternChars(),
                                         DateFormatSymbols.patternChars);
             compiledPattern = compile(p);
             this.pattern = p;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试样例:

        public static void main(String[] args){
            SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Date date=new Date();
            System.out.println(df.format(date));
            df.applyLocalizedPattern("G");
            System.out.println(df.format(date));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试结果:

    2022-07-05 09:35:30
    公元
    
    • 1
    • 2

    确保时间安全的样例代码:

        public static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    
        public static void main(String[] args) {
            synchronized (df){
                Date date = new Date();
                System.out.println(df.format(date));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    但是final修饰仍然可以改变pattern 。

    2.DateTimeFormatter线程安全

    所有变量包括他自己都是使用final修饰的。

    public final class DateTimeFormatter {
    
        /**
         * The printer and/or parser to use, not null.
         */
        private final CompositePrinterParser printerParser;
        /**
         * The locale to use for formatting, not null.
         */
        private final Locale locale;
        /**
         * The symbols to use for formatting, not null.
         */
        private final DecimalStyle decimalStyle;
        /**
         * The resolver style to use, not null.
         */
        private final ResolverStyle resolverStyle;
        /**
         * The fields to use in resolving, null for all fields.
         */
        private final Set<TemporalField> resolverFields;
        /**
         * The chronology to use for formatting, null for no override.
         */
        private final Chronology chrono;
        /**
         * The zone to use for formatting, null for no override.
         */
        private final ZoneId zone;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    使用样例:

    		DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
            String str1 = dateTimeFormatter.format(LocalDateTime.now());
            System.out.println(str1);
            String dateStr= "2018年12月18日";
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
            LocalDate date2= LocalDate.parse(dateStr, formatter);
            dateTimeFormatter.parse(str1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    根据查看源码,不涉及对自身属性或者方法的修改,可以认为只是方法调用,

        public void formatTo(TemporalAccessor temporal, Appendable appendable) {
            Objects.requireNonNull(temporal, "temporal");
            Objects.requireNonNull(appendable, "appendable");
            try {
                DateTimePrintContext context = new DateTimePrintContext(temporal, this);
                if (appendable instanceof StringBuilder) {
                    printerParser.format(context, (StringBuilder) appendable);
                } else {
                    // buffer output to avoid writing to appendable in case of error
                    StringBuilder buf = new StringBuilder(32);
                    printerParser.format(context, buf);
                    appendable.append(buf);
                }
            } catch (IOException ex) {
                throw new DateTimeException(ex.getMessage(), ex);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    printerParser成员是final修饰,printerParser.format方法代码如下,其中成员属性optional也是final修饰,

     public boolean format(DateTimePrintContext context, StringBuilder buf) {
                int length = buf.length();
                if (optional) {
                    context.startOptional();
                }
                try {
                    for (DateTimePrinterParser pp : printerParsers) {
                        if (pp.format(context, buf) == false) {
                            buf.setLength(length);  // reset buffer
                            return true;
                        }
                    }
                } finally {
                    if (optional) {
                        context.endOptional();
                    }
                }
                return true;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    通过查询发现parse也是如此。

  • 相关阅读:
    CityEngine记录1:工程目录
    nginx相关解析
    git revert 撤销之前的提交
    常见的软件脱壳思路
    【交易心态】MT4外汇交易秘籍:如何保持冷静应对波动市场?
    入职后快速配置mac方便快速上手业务for研测向
    Nginx七层负载均衡之动静分离
    使用git将本地文件上传到仓库+git常用指令
    17-Explain执行计划-01
    Linux服务器实验总结以及回顾(全)
  • 原文地址:https://blog.csdn.net/a1773570500/article/details/125628367