This article has been authorized by the author Yao Taihang NetEase cloud community.

Welcome to visit NetEase Cloud Community to learn more about NetEase’s technical product operation experience.


SimpleDateFormat is used extensively to handle time formatting. Since the class is created with a pattern for a fixed time format, it is common to create an object with a large scope (static modifier or some kind of private attribute) for reuse. Since the use of multithreading concurrency during time transitions is rare, it is difficult to see the pitfalls in this class. In fact, this class is not thread-safe and may encounter problems when using the Format () and parse() methods for multithreading.


Analysis of the

In the source file of SimpleDateFormat and its parent, DateFormat, there is a paragraph stating:


* Date formats are not synchronized.
* It is recommended to create separate format instances for each thread.
* If multiple threads access a format concurrently, it must be synchronized* externally.Copy the code


The JDK documentation makes it clear that both of these classes are non-thread-safe during time formatting. That is, using the same SimpleDateFormat instance and opening several threads to do date conversions may not yield accurate results.


parse

Parse () tests, I used the following test code (JDk1.8) in reference to experiments done by others:


import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class DateFormatTest extends Thread { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");    private String name;    private String dateStr;    public DateFormatTest(String name, String dateStr) {        this.name = name;        this.dateStr = dateStr;
    }    @Override
    public void run() {

        Date date = null;        try {
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        System.out.println(name + " : date: " + date);
    }    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();

        executor.execute(new DateFormatTest("Test_A"."2000-04-28"));
        executor.execute(new DateFormatTest("Test_B"."2017-04-28")); executor.shutdown(); }}Copy the code


This test code refers to a use case on the Internet. Different from the original use case, the thread wait Sleep was performed between two thread operations. In order to see the effect, the modified test case removed the thread wait part. Although the result of each run is different, the exception that is often thrown is:


Exception in thread "pool-1-thread-1" java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Long.parseLong(Long.java:601)
    at java.lang.Long.parseLong(Long.java:631)
    at java.text.DigitList.getLong(DigitList.java:195)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    at java.text.DateFormat.parse(DateFormat.java:364)
    at DateFormatTest.run(DateFormatTest.java:24)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Test_B : date: Mon Apr 28 00:00:00 CST 2200Copy the code


To be clear, the string to be converted is held by each object as a non-static private variable, and only the SDF itself is public. It is not hard to see that even if the output is successful, the value may not be correct, and the parse() method is not secure.


format

SimpleDateFormat format() method source:


private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); .Copy the code


It is important to note that Calendar operations are not thread-safe, and format is clearly not safe to use in concurrent scenarios. The testing process is similar to that of the Parse process, so I won’t go into details here.


To solve

Since SimpleDateFormat itself is not secure, there are two ways to solve the problem: optimize the usage process or find alternatives.


1. Create a temporary vm

Instead of using Static, a new instance is created each time it is used.


Existing problems:

SimpleDateFormat uses a Calendar object, which is very heavy. Under high concurrency, a large number of new SimpleDateFormat and destruction of SimpleDateFormat will consume resources.


2.synchronized

Synchronizes SimpleDateFormat objects with synchronized.


Existing problems:

At high concurrency, using the object blocks and other users wait while the current user uses it, and even though the result is correct, the concurrency becomes queued, which does not actually solve the problem and affects performance and efficiency.


3.ThreadLocal

With ThreadLocal, have each thread create an instance object of the current thread’s SimpleDateFormat.


Existing problems:

With ThreadLocal, if the process of executing atomic tasks is one task per thread, this declaration is basically the same as creating instance objects before each use. If you are using multiple threads plus a task queue, for example, Tomcat has m processing threads and n external task requests, then when executing n tasks, only M instances of SimpleDateFormat will be created. For a single processing thread, the task execution is ordered, so for the current thread, There is no concurrency.


Apache DateFormatUtils and FastDateFormat

Use org.apache.com mons. Lang. Time. FastDateFormat and org.apache.com mons. Lang. Time. DateFormatUtils.


Existing problems:

Apache promises to be thread-safe and more efficient. DateFormatUtils and FastDateFormat only have format() methods. All format methods only accept long, Date, and Calendar inputs and convert them to time strings. Parse () does not currently exist. A time string can be converted into a time object.


5.Joda-Time

Use the Joda-time library.


Existing problems:

No problem ~


Brief introduction:

Joda-time – An alternative to date/Time libraries for Java applications, joda-time makes Time and date values easy to manage, manipulate, and understand. In fact, ease of use is a major design goal of Joda. Other goals include scalability, a complete feature set, and support for multiple calendar systems. And Joda is 100 percent interoperable with the JDK, so you don’t need to replace all of your Java code, just the part that performs the date/time calculation.


Information:

Joda-time www.ibm.com/developerwo… Joda-time documentation (English) joda-time.sourceforge.net/



“To see, to look everywhere.” – Song Fan


Free experience cloud security (EASY Shield) content security, verification code and other services

For more information about NetEase’s technology, products and operating experience, please click here.


【 Recommended 】 Data warehouse construction of the six pulse magic sword