Home  >  Article  >  Java  >  Why is simpledateformat thread unsafe?

Why is simpledateformat thread unsafe?

青灯夜游
青灯夜游Original
2021-05-10 14:40:2417240browse

Cause: In a multi-threaded environment, when multiple threads use the same SimpleDateFormat object (such as static modification) at the same time, such as calling the format method, multiple threads will call the calender.setTime method at the same time, resulting in time Modified by other threads, so the thread is unsafe.

Why is simpledateformat thread unsafe?

The operating environment of this tutorial: windows7 system, java8 version, DELL G3 computer.

Thread unsafe verification:

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));

    public void test() {
        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);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

Output:

  true
  false
  true
  true
  false

false appears, indicating that the thread is unsafe

1, format Method

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;
    }
 protected Calendar calendar;

You can see that the variable calendar is shared between multiple threads and the calendar is modified. Therefore, in a multi-threaded environment, when multiple threads use the same SimpleDateFormat object (such as static modification) at the same time, such as calling the format method, multiple threads will call the calender.setTime method at the same time, causing time to be modified by other threads. Therefore Threads are not safe.

In addition, the parse method is also thread-unsafe. The parse method actually calls CalenderBuilder's establish for parsing. The main steps in the method are not atomic operations.

Solution:

1. Define SimpleDateFormat as a local variable

2. Add a thread synchronization lock: synchronized(lock)

 3. Using ThreadLocal, each thread has its own copy of the SimpleDateFormat object. Such as:

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
        private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    //    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    SimpleDateFormat simpleDateFormat = THREAD_LOCAL.get();
                    if (simpleDateFormat == null) {
                        simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    }
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    } finally {
                        local.remove();
                    }
                }
            });
        }
    }
}

4. Use DateTimeFormatter instead of SimpleDateFormat

DateTimeFormatter is thread-safe and provides many formatting methods by default. Custom formatting methods can also be created through the ofPattern method.

  (1) Format date example:

 LocalDateTime localDateTime = LocalDateTime.now();
 System.out.println(localDateTime); // 2019-11-20T15:04:29.017
 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 String strDate=localDateTime.format(dtf);
 System.out.println(strDate); // 2019/23/20 15:23:46

  (2) Parse date

 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
 System.out.println(localDateTime); // 2019-11-20T15:23:46

Related video tutorial recommendations: Java video tutorial

The above is the detailed content of Why is simpledateformat thread unsafe?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn