SimpleDateFormat Non-thread safe

Source: Internet
Author: User
Tags string format

The problem that has not been noticed before!

Save yourself a good look.

Originating From: http://www.cnblogs.com/zemliu/p/3290585.html

1. Causes

There is a Calendar object reference inside the SimpleDateFormat (SDF) class that is used to store date information related to this SDF, such as Sdf.parse (DATESTR), Sdf.format (date) And so on. Parameters passed in date-related strings, dates, and so on, are all dating calendar is used to store. This will cause a problem, if your SDF is static, then multiple thread will share this SDF, Also share this calendar reference, and, observing the Sdf.parse () method, you will find a call like the following:

Date Parse () {  calendar.clear ();//Cleanup Calendar  ...//perform some actions, set calendar date for what  calendar.gettime ();//Get CAL Endar of Time}

The problem here is that if thread A calls Sdf.parse () and does not perform calendar.gettime () after calendar.clear (), thread B calls Sdf.parse (), At this point, thread B also executes the Sdf.clear () method, which causes the calendar data for thread A to be emptied (in fact, a, B is emptied at the same time). Or when a executes the calendar.clear () is suspended, then B begins to call Sdf.parse () and ends with a smooth I, so that the date stored in A's calendar becomes the date of the calendar that was set by B later

2. Problem Recurrence

Import Java.text.parseexception;import Java.text.simpledateformat;import Java.util.date;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;import Java.util.concurrent.TimeUnit    ;/** * @author Zhenwei.liu created on 2013 13-8-29 pm 5:35 * @version $Id $ */public Class Dateformattest extends Thread {    private static SimpleDateFormat SDF = new SimpleDateFormat ("Yyyy-mm-dd");    private String name;    Private String datestr;    Private Boolean sleep;        Public Dateformattest (string name, String Datestr, Boolean sleep) {this.name = name;        This.datestr = Datestr;    This.sleep = sleep;        @Override public void Run () {date date = null;            if (sleep) {try {TimeUnit.MILLISECONDS.sleep (2000);            } catch (Interruptedexception e) {e.printstacktrace ();        }} 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.NEWC        Achedthreadpool ();        A will sleep 2s after starting to execute Sdf.parse () Executor.execute (New Dateformattest ("A", "1991-09-13", true));        B break point, will be stuck in the middle of the method Executor.execute ("B", "2013-09-13", false));    Executor.shutdown (); }}

Execute this code using debug mode and break the breakpoint in the Sdf.parse () method

Parse () {    calendar.clear ()    //hit a breakpoint here    Calendar.gettime ()}

Process:

1) First a thread runs up and then enters sleep.

2) b thread running up and stuck at the breakpoint

3) A thread wakes up, executes Calendar.clear (), and sets the date of Sdf.calendar to 1991-09-13, at which time a B's calendar is 1991-09-13

4) Let the breakpoint continue execution, the output is as follows

A:date:fri Sep 00:00:00 CDT 1991
B:date:fri Sep 00:00:00 CDT 1991

It's not the result we're looking for.

3. Solution

The simplest solution we can remove the static so that each new thread will have its own SDF instance to avoid thread-safety issues

However, using this approach, a large amount of new SDF and the destruction of SDF in highly concurrent situations can be very resource-intensive

In the case of concurrency, the request task and thread execution of a Web site might be understood as follows

For example, Tomcat's thread pool has a maximum thread count of 4, and now there are 1000 tasks to perform (understood to have 1000 users point to a feature of your site),

And these 1000 missions will use the date we wrote. function Processing class

A) If the Date function processing class is using the new SimpleDateFormat method, then there will be 1000 SDF creation and destruction

B) Java provides a threadlocal solution that works by having only one instance per thread, which means that we're only going to instantiate 4 SDF in a total of 1000 tasks.

Moreover, it does not have multithreading concurrency problems, because a single thread execution task must be sequential, such as the thread #1负责执行Task #1-#250, then he executes the task in order #1-#250

And the thread #2拥有自己的sdf实例, he is also the task of executing tasks #251-#500, and so on

Here is an example of using threadlocal to resolve an SDF multithreading problem

Import Java.text.parseexception;import java.text.simpledateformat;import java.util.date;import java.util.HashMap;    Import java.util.map;/** * @author Zhenwei.liu created on 2013 13-8-29 pm 5:35 * @version $Id $ */public class Dateutil {    /** Lock Object */private static final Object lockobj = new Object (); /** the SDF Map */private static map<string, threadlocal<simpledateformat>> Sdfmap = new HASHMAP&L, which stores different date template formats T    String, threadlocal<simpledateformat>> (); /** * Returns a threadlocal SDF, each thread will only be new once SDF * * @param pattern * @return */private static SimpleDate        Format getsdf (final String pattern) {threadlocal<simpledateformat> TL = sdfmap.get (pattern); Here the double judgment and synchronization is to prevent sdfmap this singleton is repeatedly put repeated by the SDF if (TL = = null) {synchronized (lockobj) {tl =                Sdfmap.get (pattern); if (TL = = null) {////-only the SDF that does not have this pattern in map will generate a new SDF and put it into the map System.out.println ("Put N EW SDF of pattern "+ pattern +" to map "); Here is the key to using threadlocal<simpledateformat> instead of the original direct new SimpleDateFormat TL = new threadlocal<simple                            Dateformat> () {@Override protected SimpleDateFormat InitialValue () {                            System.out.println ("Thread:" + thread.currentthread () + "init pattern:" + pattern);                        return new SimpleDateFormat (pattern);                    }                    };                Sdfmap.put (pattern, TL);    }}} return Tl.get (); /** * is used threadlocal<simpledateformat> to get simpledateformat, so that each thread will only have a simpledateformat * * @param da TE * @param pattern * @return */public static string format (date date, String pattern) {return get    SDF (pattern). Format (date);  The public static Date parse (string datestr, String pattern) throws ParseException {      return GETSDF (pattern). Parse (DATESTR); }}

Test class

Import Java.text.parseexception;import Java.util.concurrent.executorservice;import java.util.concurrent.Executors Import java.util.concurrent.timeunit;/** * @author Zhenyu.nie created on 2013 13-8-26 pm 2:13 * @version 1.0.0 */public cl        The string[{public static void main (*. args]) {final String patten1 = "YYYY-MM-DD";        Final String patten2 = "yyyy-mm"; Thread t1 = new Thread () {@Override public void run () {try {Dat                Eutil.parse ("1992-09-13", patten1);                } catch (ParseException e) {e.printstacktrace ();        }            }        }; Thread t2 = new Thread () {@Override public void run () {try {Dat                Eutil.parse ("2000-09", patten2);                } catch (ParseException e) {e.printstacktrace ();        }            }        }; thread t3 = new Thread () {@OverRide public void Run () {try {dateutil.parse ("1992-09-13", patten1);                } catch (ParseException e) {e.printstacktrace ();        }            }        }; Thread T4 = new Thread () {@Override public void run () {try {Dat                Eutil.parse ("2000-09", patten2);                } catch (ParseException e) {e.printstacktrace ();        }            }        }; Thread T5 = new Thread () {@Override public void run () {try {Dat                Eutil.parse ("2000-09-13", patten1);                } catch (ParseException e) {e.printstacktrace ();        }            }        }; Thread T6 = new Thread () {@Override public void run () {try {Dat                Eutil.parse ("2000-09", patten2); } catch (ParseException e) {e.printstacktrace ();        }            }        };        SYSTEM.OUT.PRINTLN ("Single Thread Execution:");        Executorservice exec = Executors.newfixedthreadpool (1);        Exec.execute (t1);        Exec.execute (T2);        Exec.execute (T3);        Exec.execute (T4);        Exec.execute (T5);        Exec.execute (T6);        Exec.shutdown ();        Sleep (1000);        System.out.println ("Double-threaded execution:");        Executorservice exec2 = Executors.newfixedthreadpool (2);        Exec2.execute (t1);        Exec2.execute (T2);        Exec2.execute (T3);        Exec2.execute (T4);        Exec2.execute (T5);        Exec2.execute (T6);    Exec2.shutdown ();        } private static void sleep (long millsec) {try {TimeUnit.MILLISECONDS.sleep (millsec);        } catch (Interruptedexception e) {e.printstacktrace (); }    }}

Output

Single Thread Execution:
Put new SDF of pattern YYYY-MM-DD to map
Thread:thread[pool-1-thread-1,5,main] Init pattern:yyyy-mm-dd
Put new SDF of pattern yyyy-mm to map
Thread:thread[pool-1-thread-1,5,main] Init pattern:yyyy-mm
Dual-Thread Execution:
Thread:thread[pool-2-thread-1,5,main] Init pattern:yyyy-mm-dd
Thread:thread[pool-2-thread-2,5,main] Init pattern:yyyy-mm
Thread:thread[pool-2-thread-1,5,main] Init pattern:yyyy-mm
Thread:thread[pool-2-thread-2,5,main] Init pattern:yyyy-mm-dd

From the output we can see:

1) When 1 threads perform these 6 tasks, the thread will use the new SDF for the first time, and use this SDF in the future instead of creating a new SDF every time the task is processed.

2) 2 threads perform 6 tasks at the same time, but 2 threads of SDF are separate, each thread has its own "yyyy-mm-dd", "yyyy-mm" SDF, so they will not thread security security issues

Imagine that if you are using the new implementation method, whether it is 1 threads to execute, or 2 threads to perform these 6 tasks, all need new 6 SDF

SimpleDateFormat Non-thread safe

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.