This is the 10th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

preface

SimpleDateFormat is a thread-unsafe class. If you define it as static, you must lock it. Or use the DateUtils utility class.”

But why is SimpleDateFormat a thread-unsafe class? To demonstrate this problem, let’s first assemble an example of how SimpleDateFormat works in a multithreaded environment.

public static void main(String[] args) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); CountDownLatch countDownLatch = new CountDownLatch(30); for (int i = 0; i < 30; I++) {CompletableFuture. RunAsync (() - > {/ / to get the current time format String String str1 = simpleDateFormat. The format (new Date ()); Date parseDate = simpleDateformat.parse (str1); parseDate = simpleDateformat.parse (str1); String str2 = simpleDateFormat.format(parseDate); System.out.println(string. format("threadId = %s, before = %s, after = %s", thread.currentThread ().getid (), str1, str2)); } catch (Exception ignored) { } finally { countDownLatch.countDown(); } }, ThreadPoolUtil.pool); } countDownLatch.await(); }Copy the code

New Date() = new Date() = new Date(); Same, it means that the site is safe. Let’s look at the results of the run



It turns out that in some threads, time is inconsistent. That means SimpleDateFormat is indeed thread-unsafe.

Take a look at the source of the format method

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;
}
Copy the code

As you can see, multiple threads share the variable Calendar and modify calendar. Therefore, in a multi-threaded environment, when multiple threads use the same SimpleDateFormat object (such as static modifier) at the same time, for example, when the format method is called, multiple threads will call calender.setTime at the same time, causing time to be modified by other threads. Therefore, threads are not safe. In addition, the parse method is not thread-safe. The parse method actually calls CalenderBuilder’s ESTABLISH to parse, and atomic operations are not the main steps in the method. To avoid this, either lock or inside the method new SimpleDateFormat. Either way, it has an impact on performance. JDK8’s DateTimeFormatter is a good solution. Let’s take a look at how the DateTimeFormatter is thread-safe.

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;
Copy the code

The DateTimeFormatter class is final, and the member variables are final. This means that the DateTimeFormatter is immutable, meaning that thread B objects cannot modify variables in thread A’s DateTimeFormatter objects. Thread B can only create a DateTimeFormatter object of its own.

conclusion

After briefly explaining why SimpleDateFormat is thread-unsafe and DateTimeFormatter is thread-safe, we should try to standardize our code in practical development and understand its principles when using encapsulated utility classes to avoid problems. If your project is JDK8, try using DateTimeFormatter directly when dealing with time issues!