I. Why is SIMPLEDATEFORMAT not thread-safe?
Java source code is as follows:
/*** Date Formats is isn't synchronized.* It's recommended to create separate format instances for each thread.* If multiple threads Access a format concurrently, it must be synchronized* Externally.*/public class SimpleDateFormat extends DateFormat {pub Lic Date Parse (String text, parseposition pos) {calendar.clear ();//Clears all the time fields//other logic ... Date parseddate = Calendar.gettime ();}} Abstract class dateformat{//Other logic ... protected Calendar calendar;public Date Parse (String source) throws Parseexcep tion{parseposition pos = new parseposition (0); Date result = Parse (source, POS); if (Pos.index = = 0) throw new ParseException ("Unparseable Date: \" "+ Source +" \ "", Pos.errori NDEX); return result; }}
If we define SimpleDateFormat as a static member variable, the SDF object is shared between multiple thread, so the calendar object is also shared.
Assuming that thread A and thread B both enter the parse (text, pos) method, thread B executes to calendar.clear () and thread A executes to Calendar.gettime (), then there is a problem.
Two. The problem recurs:
public class Dateformattest {private static SimpleDateFormat SDF = new SimpleDateFormat ("dd-mmm-yyyy", locale.us); private static String date[] = {"01-jan-1999", "01-jan-2000", "01-jan-2001"};p ublic static void Main (string[] args) {for (int i = 0; i < date.length; i++) {Final int temp = i;new Thread (new Runnable () {@Overridepublic void run () {try {while (true) {String str1 = date[temp]; String str2 = Sdf.format (Sdf.parse (str1)); System.out.println (Thread.CurrentThread (). GetName () + "," + str1 + "," + str2 "), if (!str1.equals (str2)) {throw new Runtimee Xception (Thread.CurrentThread (). GetName () + ", expected" + str1 + "but got" + str2);}}} catch (Exception e) {throw new RuntimeException ("Parse failed", e);}}). Start ();}}}
Create three processes, use the static member variable SimpleDateFormat the parse and format methods, and compare the values after these two methods toss to be equal:
If the following error occurs, the incoming date is dropped, that is, the SimpleDateFormat is not thread safe:
Exception in Thread "Thread-0" Java.lang.RuntimeException:parse failedat Cn.test.dateformattest$1.run ( dateformattest.java:27) at Java.lang.Thread.run (thread.java:662) caused by:java.lang.runtimeexception:thread-0, Expected 01-jan-1999 but got 01-jan-2000at cn.test.dateformattest$1.run (dateformattest.java:22) ... 1 more
Three. Solution:
1. Solution A:
Define SimpleDateFormat as a local variable:
SimpleDateFormat SDF = new SimpleDateFormat ("dd-mmm-yyyy", locale.us); String str1 = "01-jan-2010"; String str2 = Sdf.format (Sdf.parse (str1));
Disadvantage: A SimpleDateFormat object is created each time the method is called, and the method ends with garbage collection.
2. Solution B:
Add a thread Sync Lock: Synchronized (lock)
public class Syncdateformattest {private static SimpleDateFormat SDF = new SimpleDateFormat ("dd-mmm-yyyy", locale.us); private static String date[] = {"01-jan-1999", "01-jan-2000", "01-jan-2001"};p ublic static void Main (string[] args) {for (int i = 0; i < date.length; i++) {Final int temp = i;new Thread (new Runnable () {@Overridepublic void run () {try {while (true) {synchronized (SDF) {String s TR1 = Date[temp];D ate date = Sdf.parse (STR1); String str2 = Sdf.format (date); System.out.println (Thread.CurrentThread (). GetName () + "," + str1 + "," + str2 "), if (!str1.equals (str2)) {throw new Runtimee Xception (Thread.CurrentThread (). GetName () + ", expected" + str1 + "but got" + str2);}}} catch (Exception e) {throw new RuntimeException ("Parse failed", e);}}). Start ();}}}
Cons: Poor performance, waiting for other threads to enter after the lock is released each time
3. Solution C: (recommended)
Use threadlocal: Each thread will have its own copy of the SimpleDateFormat object.
Write a tool class:
public class Dateutil {private static threadlocal<simpledateformat> local = new Threadlocal<simpledateformat > ();p ublic static Date parse (String str) throws Exception {SimpleDateFormat SDF = Local.get (); if (SDF = = null) {SDF = n EW SimpleDateFormat ("dd-mmm-yyyy", locale.us); Local.set (SDF); return Sdf.parse (str);} public static String format (date date) throws Exception {SimpleDateFormat SDF = Local.get (); if (SDF = = null) {SDF = new Si Mpledateformat ("dd-mmm-yyyy", locale.us); Local.set (SDF); return Sdf.format (date);}}
Test code:
public class Threadlocaldateformattest {private static String date[] = {"01-jan-1999", "01-jan-2000", "01-jan-2001"};p UB Lic static void Main (string[] args) {for (int i = 0; i < date.length; i++) {final int temp = i;new Thread (New Runnable ( {@Overridepublic void Run () {try {while (true) {String str1 = date[temp];D ate date = Dateutil.parse (STR1); String str2 = Dateutil.format (date); System.out.println (str1 + "," + str2), if (!str1.equals (str2)) {throw new RuntimeException (Thread.CurrentThread (). GetName () + ", expected" + str1 + "but got" + str2);}} catch (Exception e) {throw new RuntimeException ("Parse failed", e);}}). Start ();}}}
SimpleDateFormat non-threading security and workarounds