SimpleDateFormat is a very common class in Java that is used to parse and format date strings. But SimpleDateFormat in the multi-threaded environment is not safe, this is very easy to make mistakes in the part, the next talk about the process of this problem and the solution of the idea.
Problem Description:
Look at the code first to get a one-month number of days:
ImportJava.text.SimpleDateFormat;ImportJava.util.Calendar;Importjava.util.Date; Public classDateutil {/*** Get the number of months *@paramTime 201202 *@return */ Public Static intGetDays (String time)throwsException {//String time = "201202";SimpleDateFormat SDF =NewSimpleDateFormat ("Yyyymm"); Date Date=Sdf.parse (time); Calendar C=calendar.getinstance (); C.settime (date); intDay =C.getactualmaximum (calendar.date); returnDay ; } }
You can see in this method, each time you want to get the value of the first time to create a SimpleDateFormat instance, frequently call this method in the case of a performance-consuming. In order to avoid the frequent creation and destruction of a large number of instances, we usually use a singleton or static variable to modify it, which is generally the case:
ImportJava.text.SimpleDateFormat;ImportJava.util.Calendar;Importjava.util.Date; Public classDateutil {Private StaticSimpleDateFormat SDF =NewSimpleDateFormat ("Yyyymm"); /*** Get the number of months *@paramTime 201202 *@return */ Public Static intGetDays (String time)throwsException {//String time = "201202"; Date Date =Sdf.parse (time); Calendar C=calendar.getinstance (); C.settime (date); intDay =C.getactualmaximum (calendar.date); returnDay ; } }
At this point, no matter how many times this method, the Java virtual machine only a SimpleDateFormat object, the efficiency and performance is certainly better than the first method, this is a lot of programmers choose the method. However, in this multi-threaded condition, multiple threads share the same simpledateformat, and SimpleDateFormat itself is thread-insecure, which makes it easy to make a variety of problems.
To verify the problem:
Use a simple example to verify the results of the SimpleDateFormat operation in a multithreaded environment:
Importjava.text.ParseException;ImportJava.text.SimpleDateFormat;Importjava.util.Date;ImportJava.util.concurrent.CountDownLatch; Public classDateutil {Private StaticSimpleDateFormat DateFormat =NewSimpleDateFormat ("Yyyy-mm-dd HH:mm:ss"); Public StaticString Format (date date) {returnDateformat.format (date); } Public StaticDate Parse (String datestr)throwsParseException {returnDateformat.parse (DATESTR); } Public Static voidMain (string[] args) {FinalCountdownlatch latch =NewCountdownlatch (1); Finalstring[] STRs =NewString[] {"2016-01-01 10:24:00", "2016-01-02 20:48:00", "2016-01-11 12:24:00"}; for(inti = 0; I < 10; i++) { NewThread (NewRunnable () {@Override Public voidrun () {Try{latch.await (); } Catch(interruptedexception e) {e.printstacktrace (); } for(inti = 0; I < 10; i++){ Try{System.out.println (Thread.CurrentThread (). GetName ()+ "\ T" + Parse (strs[i%strs.length])); Thread.Sleep (100); } Catch(ParseException e) {e.printstacktrace (); } Catch(interruptedexception e) {e.printstacktrace (); }}}). Start (); } latch.countdown (); }}
Look at the results of the run:
Thread-9 Fri Jan 10:24:00 CST 2016Thread-1 Sat 00:48:00 CST 20162017Thread-5 Sat 00:48:00 CST 20162017Exception in Thread"Thread-4" Exception in Thread "Thread-6" Java.lang.NumberFormatException:For input string: "2002.e20022e"At sun.misc.FloatingDecimal.readJavaFormatString (Floatingdecimal.java:2043) at sun.misc.FloatingDecimal.parseDouble (Floatingdecimal.java:110) at java.lang.Double.parseDouble (Double.java:538) at java.text.DigitList.getDouble (Digitlist.java:169) at Java.text.DecimalFormat.parse (Decimalformat.java:2056) at Java.text.SimpleDateFormat.subParse (Simpledateformat.java:2162) at Java.text.SimpleDateFormat.parse (Simpledateformat.java:1514) at Java.text.DateFormat.parse (Dateformat.java:364) at Dateutil.parse (Dateutil.java:24) at dateutil$2.run (dateutil.java:45)
So why is SimpleDateFormat not thread-safe?
To find the problem:
First look at the source code of SimpleDateFormat:
Privatestringbuffer Format (date date, StringBuffer toappendto, Fielddelegate delegate) {//Convert Input Date to TIME field listcalendar.settime (date); BooleanUsedateformatsymbols =Usedateformatsymbols (); for(inti = 0; I <compiledpattern.length;) { intTag = Compiledpattern[i] >>> 8; intCount = compiledpattern[i++] & 0xff; if(Count = = 255) {Count= compiledpattern[i++] << 16; Count|= compiledpattern[i++]; } Switch(tag) { CaseTAG_QUOTE_ASCII_CHAR:toAppendTo.append ((Char) count); Break; CaseTAG_QUOTE_CHARS:toAppendTo.append (Compiledpattern, I, count); I+=count; Break; default: Subformat (Tag, count, delegate, Toappendto, Usedateformatsymbols); Break; } } returntoappendto;}
You can see that the format () method first stores the date in a Calendar object, and the calendar exists as a member variable in SimpleDateFormat. The member variable calendar is then used again when Subformat () is called. This is where the problem lies. Similarly, the parse () method also has a corresponding problem.
Imagine, in a multithreaded environment, if two threads all use the same SimpleDateFormat instance, then it is possible that one of the threads modifies the calendar immediately after the other thread has modified the calendar, Then the first thread that was used in the calendar was not the value it expected.
Avoid problems:
So, how to ensure simpledateformat thread safety?
1. Each time you use SimpleDateFormat, create a local SimpleDateFormat object, just like the one at the beginning, but there are performance problems and overhead.
2. Lock or Sync
Importjava.text.ParseException;ImportJava.text.SimpleDateFormat;Importjava.util.Date; Public classDatesyncutil {Private StaticSimpleDateFormat SDF =NewSimpleDateFormat ("Yyyy-mm-dd HH:mm:ss"); Public StaticString formatdate (date date)throwsparseexception{synchronized(SDF) {returnSdf.format (date); } } Public StaticDate Parse (String strdate)throwsparseexception{synchronized(SDF) {returnSdf.parse (strdate); } } }
When the thread is more, when a thread calls the method, other threads that want to call this method will block, and multithreading concurrency will have a certain effect on performance.
3. Using threadlocal
Public classDateutil {Private StaticThreadlocal<simpledateformat> local =NewThreadlocal<simpledateformat>() {@OverrideprotectedSimpleDateFormat InitialValue () {return NewSimpleDateFormat ("Yyyy-mm-dd HH:mm:ss"); } }; Public StaticString Format (date date) {returnlocal.get (). Format (date); } Public StaticDate Parse (String datestr)throwsParseException {returnLocal.get (). Parse (DATESTR); }}
Using threadlocal ensures that each thread can get a separate SimpleDateFormat object, avoiding the frequent creation of objects and the contention of multithreading.
Simpledateformat,calendar thread non-security issues