About SimpleDateFormat security time-formatted thread safety issues

Source: Internet
Author: User
Tags dateformat string format stringbuffer

Presumably everyone is not unfamiliar with SimpleDateFormat. SimpleDateFormat is a very common class in Java that is used to parse and format date strings, but it can cause very subtle and difficult-to-debug problems if used accidentally, because DateFormat and SimpleDateFormat Classes are not thread-safe, calling the format () and Parse () methods in a multithreaded environment should use synchronous code to avoid problems. Below we take a concrete scene to learn and understand the SimpleDateFormat class in depth step-by-step.

A. Intro
We are all good programmers and we all know that we should create SimpleDateFormat instances as few as possible in our program, because creating such an instance takes a lot of cost. In an example of a read database data export to an Excel file, each time the information is processed, it is necessary to create a SimpleDateFormat instance object, and then discard the object. A large number of objects are created so that they occupy a lot of memory and JVM space. The code is as follows:

Package Com.peidasoft.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Dateutil {public        static  String formatdate (date date) throws parseexception{         SimpleDateFormat SDF = new SimpleDateFormat ("Yyyy-mm-dd HH:mm:ss");        return Sdf.format (date);    }        public static Date Parse (String strdate) throws parseexception{         SimpleDateFormat sdf = new SimpleDateFormat (" Yyyy-mm-dd HH:mm:ss ");        Return Sdf.parse (strdate);}    }

You might say, OK, then I'll create a static SimpleDateFormat instance and put it in a Dateutil class (below) and use that instance directly when using it, so the problem is solved. The improved code is as follows:

Package Com.peidasoft.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Dateutil {    private static final  simpledateformat SDF = new SimpleDateFormat (" Yyyy-mm-dd HH:mm:ss ");        public static  String formatdate (date date) throws parseexception{        return Sdf.format (date);    }        public static Date Parse (String strdate) throws parseexception{        return Sdf.parse (strdate);}    }

Of course, this method is really good, in most of the time will work very well. But when you're working in a production environment for a while, you'll find the fact that it's not thread-safe. In the normal test situation, there is no problem, but once in the production environment under a certain load situation, the problem comes out. He will appear in a variety of situations, such as incorrect time for conversion, such as an error, such as a thread being hung dead, etc. Let's look at the following test case, the fact that says:

Package Com.peidasoft.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Dateutil {        private static final  simpledateformat SDF = new SimpleDateFormat (" Yyyy-mm-dd HH:mm:ss ");        public static  String formatdate (date date) throws parseexception{        return Sdf.format (date);    }        public static Date Parse (String strdate) throws parseexception{        return Sdf.parse (strdate);}    }
Package Com.peidasoft.dateformat;import Java.text.parseexception;import Java.util.date;public class DateUtilTest { Public        Static Class Testsimpledateformatthreadsafe extends Thread {        @Override public        void Run () {            while (true) {                try {                    this.join],                } catch (Interruptedexception E1) {                    e1.printstacktrace ();                }                try {                    System.out.println (this.getname () + ":" +dateutil.parse ("2013-05-24 06:02:20"));                } catch (ParseException e) {                    e.printstacktrace ();            }}}} public static void Main (string[] args) {for        (int i = 0; i < 3; i++) {            new Testsimpledateformatthreadsafe (). STA RT ();}}}                

The execution output is as follows:

Exception in Thread "Thread-1" java.lang.NumberFormatException:multiple points at Sun.misc.FloatingDecimal.readJavaFo Rmatstring (floatingdecimal.java:1082) at java.lang.Double.parseDouble (double.java:510) at Java.text.DigitList.getDouble (digitlist.java:151) at Java.text.DecimalFormat.parse (decimalformat.java:1302) at Java.text.SimpleDateFormat.subParse (simpledateformat.java:1589) at Java.text.SimpleDateFormat.parse ( simpledateformat.java:1311) at Java.text.DateFormat.parse (dateformat.java:335) at Com.peidasoft.orm.dateformat.DateNoStaticUtil.parse (datenostaticutil.java:17) at Com.peidasoft.orm.dateformat.dateutiltest$testsimpledateformatthreadsafe.run (DateUtilTest.java:20) Exception in Thread "Thread-0" Java.lang.NumberFormatException:multiple points at Sun.misc.FloatingDecimal.readJavaFormatString ( floatingdecimal.java:1082) at java.lang.Double.parseDouble (double.java:510) at Java.text.DigitList.getDouble ( digitlist.java:151) at Java.text.DecimalForMat.parse (decimalformat.java:1302) at Java.text.SimpleDateFormat.subParse (simpledateformat.java:1589) at Java.text . Simpledateformat.parse (simpledateformat.java:1311) at Java.text.DateFormat.parse (dateformat.java:335) at Com.peidasoft.orm.dateformat.DateNoStaticUtil.parse (datenostaticutil.java:17) at Com.peidasoft.orm.dateformat.dateutiltest$testsimpledateformatthreadsafe.run (DateUtilTest.java:20) Thread-2:Mon May 24 06:0 06:02:20 CST 2021thread-2:fri-06:02:20 CST 2013thread-2:fri may 06:02:20 CST 2013thread-2:fri 2:20 CST 2013

Description: Thread-1 and Thread-0 reported java.lang.NumberFormatException:multiple points error, directly hanging dead, not up; Thread-2 although not dead, but the output time is wrong, For example, we entered the time is: 2013-05-24 06:02:20, when the output: Mon may 06:02:20 CST 2021 such a supernatural event.

Two. Causes

As a professional programmer, we all know that the overhead of sharing a variable is much smaller than creating a new variable every time. The above optimized static version of SimpleDateFormat, where there are various supernatural errors in the concurrency situation, is because the SimpleDateFormat and DateFormat classes are not thread-safe. The reason we ignore thread safety is because the interfaces that SimpleDateFormat and DateFormat provide to us do not show how it is related to thread safety. Just at the bottom of the JDK documentation is the following description:

The date format in SimpleDateFormat is not synchronized. It is recommended (recommended) to create a separate format instance for each thread. If multiple threads access a format at the same time, it must remain externally synchronized.

The JDK original document is as follows:
Synchronization:
Date formats is not synchronized.
It is recommended to the create separate format instances for each thread.
If Multiple threads access a format concurrently, it must be synchronized externally.

Let's look at the JDK source code to see why the SimpleDateFormat and DateFormat classes are not the real reason for thread safety:

SimpleDateFormat inherits DateFormat, which defines the object of the Calendar class for a protected attribute in DateFormat: Calendar. Just because the calendar tired concept complex, involved in time zone and localization and so on, the JDK implementation of the use of member variables to pass parameters, which results in a multi-threaded error occurs.

In the format method, there is a code like this:

Private StringBuffer Format (date date, StringBuffer toappendto,                                fielddelegate delegate) {        //Convert input Date t O 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++] <<;        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;    }

Calendar.settime (date) This statement changes the calendar, and later, the calendar is used (in the Subformat method), which is the root cause of the problem. Imagine that in a multithreaded environment, two threads hold an instance of the same simpledateformat, calling the Format method, respectively:
Thread 1 calls the Format method, which changes the Calendar field.
The interruption came.
Thread 2 starts execution and it also changes the calendar.
was interrupted again.
Thread 1 comes back, and at this point, the calendar is not the value it set, but the path of thread 2 design. If more than one thread competes for the Calendar object at the same time, there are various problems, such as incorrect timing, thread hangs, and so on.
Analysis of the implementation of format, we are not difficult to find that the use of member variables calendar, the only advantage is that when the call Subformat, the lack of a parameter, but brought many of these problems. In fact, as long as here with a local variable, all the way down, all the problems will be solved.
There is a more important problem behind this question-stateless: one of the benefits of a stateless approach is that it can be safely invoked in a variety of environments. To measure whether a method is stateful, see if it changes something else, such as a global variable, such as an instance field. The Format method changes the Calendar field of the SimpleDateFormat during the run, so it is stateful.

This also reminds us to note the next three points when developing and designing the system:

1. When you write a common class, make a clear explanation of the consequences of the multi-threaded call case in the comments

2. Be aware of thread safety for each shared mutable variable in a threaded environment

3. Our classes and methods are designed to be as stateless as possible.

Three. Workaround

1. Create a new instance when needed:

Package Com.peidasoft.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Dateutil {public        static  String formatdate (date date) throws parseexception{         SimpleDateFormat SDF = new SimpleDateFormat ("Yyyy-mm-dd HH:mm:ss");        return Sdf.format (date);    }        public static Date Parse (String strdate) throws parseexception{         SimpleDateFormat sdf = new SimpleDateFormat (" Yyyy-mm-dd HH:mm:ss ");        Return Sdf.parse (strdate);}    }

Note: When you need to use a new instance of SimpleDateFormat, you can avoid multithreading problems whenever you change the thread security problem object from shared to local private, but it also adds to the burden of creating objects. In general, this is actually less noticeable than the performance impact.

2. Using sync: Synchronizing SimpleDateFormat objects

Package Com.peidasoft.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Datesyncutil {    private static SimpleDateFormat SDF = new SimpleDateFormat ("Yyyy-mm-dd HH : Mm:ss ");          public static String formatdate (date date) throws parseexception{        synchronized (SDF) {            return Sdf.format (date);        }      } public        static Date parse (String strdate) throws parseexception{        synchronized (SDF) {            return Sdf.parse (strdate);        }    } }

Note: When a thread calls this method when a thread is many, other threads that want to call this method will block, and multithreading concurrency can have a certain effect on performance.

3. Use threadlocal:

Package Com.peidasoft.dateformat;import Java.text.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Concurrentdateutil {    private static ThreadLocal <DateFormat> threadLocal = new threadlocal<dateformat> () {        @Override        protected DateFormat InitialValue () {            return new SimpleDateFormat ("Yyyy-mm-dd HH:mm:ss");        }    ;    public static Date Parse (String datestr) throws ParseException {        return Threadlocal.get (). Parse (DATESTR);    }    public static String format (date date) {        return Threadlocal.get (). Format (date);}    }

Another way to do this:

Package Com.peidasoft.dateformat;import Java.text.dateformat;import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;public class Threadlocaldateutil {    private static final String Date_format = "Yyyy-mm-dd HH:mm:ss";    
public static DateFormat GetDateFormat () { DateFormat df = Threadlocal.get (); if (df==null) { df = new SimpleDateFormat (date_format); Threadlocal.set (DF); } return df; } public static String formatdate (date date) throws ParseException { return GetDateFormat (). Format (date); public static Date Parse (String strdate) throws ParseException { return GetDateFormat (). Parse (strdate); } }

Description: Using threadlocal, the shared variable is also changed to exclusive, the thread exclusive can be compared to the method exclusive in the concurrency environment can reduce the cost of many objects created. This approach is generally recommended if performance requirements are high.

4. Discard the JDK and use the time formatting classes from other class libraries:

1. Using Apache Commons Fastdateformat, claiming to be both fast and thread-safe simpledateformat, unfortunately it can only format the date, cannot parse the date string.

2. Use the Joda-time class library to handle time-related issues

To do a simple stress test, the method of the slowest, method three the fastest, but even the slowest method a performance is not bad, the general system method one and the method two can be satisfied, so that in this point is difficult to become the bottleneck of your system. From a simple point of view, it is recommended to use method one or method two, if necessary, the pursuit of a bit of performance improvement, you can consider using method three, with threadlocal cache.

Joda-time class Library is perfect for time processing, it is recommended.

Resources:

1.http://dreamhead.blogbus.com/logs/215637834.html

2.http://www.blogjava.net/killme2008/archive/2011/07/10/354062.html

Source: http://www.cnblogs.com/peida/archive/2013/05/31/3070790.html

(go) Time-formatted thread safety issues about SimpleDateFormat security

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.