On the non thread safety problem of simpledateformat and its solution

A super detailed tutorial: springboot integrates mybatis plus>>>

Some colleagues have fallen into this pit several times before. Today, I just saw a post, so I just want to sort it out~

1. Question:

Let’s take a look at a code that may cause an error

package test.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class ProveNotSafe {
	static SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
	static String testdata[] = { "01-Jan-1999", "14-Feb-2001", "31-Dec-2007" };

	public static void main(String[] args) {
		Runnable r[] = new Runnable[testdata.length];
		for (int i = 0; i < r.length; i++) {
			final int i2 = i;
			r[i] = new Runnable() {
				public void run() {
					try {
						for (int j = 0; j < 1000; j++) {
							String str = testdata[i2];
							String str2 = null;
							/* synchronized(df) */{
								Date d = df.parse(str);
								str2 = df.format(d);
								System.out.println("i: " + i2 + "\tj: " + j + "\tThreadID: "
										+ Thread.currentThread().getId() + "\tThreadName: "
										+ Thread.currentThread().getName() + "\t" + str + "\t" + str2);
							}
							if (!str.equals(str2)) {
								throw new RuntimeException("date conversion failed after " + j
										+ " iterations. Expected " + str + " but got " + str2);
							}
						}
					} catch (ParseException e) {
						throw new RuntimeException("parse failed");
					}
				}
			};
			new Thread(r[i]).start();
		}
	}
}

Results (random failure)

i: 2	j: 0	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 1	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 2	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 3	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 4	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 5	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 6	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 7	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 8	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 9	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 10	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 11	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 12	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 13	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 14	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 15	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 16	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	31-Dec-2007
i: 2	j: 17	ThreadID: 10	ThreadName: Thread-2	31-Dec-2007	11-Jan-1999
i: 0	j: 0	ThreadID: 8	ThreadName: Thread-0	01-Jan-1999	11-Jan-1999
Exception in thread "Thread-2" i: 1	j: 0	ThreadID: 9	ThreadName: Thread-1	14-Feb-2001	11-Jan-2001
Exception in thread "Thread-0" java.lang.RuntimeException: date conversion failed after 0 iterations. Expected 01-Jan-1999 but got 11-Jan-1999
	at test.date.ProveNotSafe$1.run(ProveNotSafe.java:30)
	at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-1" java.lang.RuntimeException: date conversion failed after 0 iterations. Expected 14-Feb-2001 but got 11-Jan-2001
	at test.date.ProveNotSafe$1.run(ProveNotSafe.java:30)
	at java.lang.Thread.run(Thread.java:619)
java.lang.RuntimeException: date conversion failed after 17 iterations. Expected 31-Dec-2007 but got 11-Jan-1999
	at test.date.ProveNotSafe$1.run(ProveNotSafe.java:30)
	at java.lang.Thread.run(Thread.java:619)

Well, you know the reason. This is caused by the non thread safety problem of simpledateformat

Now let’s simplify the problem. The wrong code should look like this:

import java.text.SimpleDateFormat;
import java.util.Date;
 
public class DateUtil {
  
 SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
  
 public String formatDate(Date input) {
      return sdf.format(input);
 }
  
}

2. Solutions

(1) using local variables:

0

import java.text.SimpleDateFormat;
import java.util.Date;
 
public class DateUtil {
  
 private static final String SIMPLE_FORMAT = "dd/MM/yyyy";
  
 public String formatDate(Date input) {
   
  if(input == null){
   return null;
  }
   
  SimpleDateFormat sdf = new SimpleDateFormat(SIMPLE_FORMAT);//local variable
  return sdf.format(input);
 }
}

Well, it’s thread safe, isn’t it

(2) ThreadLocal

is used

Here, each thread will have its own copy of simpledateformat

import java.text.SimpleDateFormat;
import java.util.Date;
 
public class DateUtil {
 
 // anonymous inner class. Each thread will have its own copy of the SimpleDateFormat
 private final static ThreadLocal<simpledateformat> tl = new ThreadLocal<simpledateformat>() {
  protected SimpleDateFormat initialValue() {
   return new SimpleDateFormat("dd/MM/yyyy");
  }
 } 
public String formatDate(Date input) {
  if (input == null) {
   return null;
  }
 
  return tl.get().format(input);
 }
}

PS: by the way, talk about ThreadLocal:

According to my understanding, ThreadLocal is a map container, whose key is the current thread, and value is an object that we want to ensure data security and consistency. From its function, it should be called thread local variable

Please refer to the following two abstracts: ThreadLocal

http://my.oschina.net/huangyong/blog/159489

http://my.oschina.net/huangyong/blog/159725

(3) synchronized code block

Or use decorator design pattern to wrap simpledateformat to make it thread safe

(4) use the date processing function of the third party:

For example, joda can avoid these problems. You can also use the fastdateformat tool class in the Commons Lang package

(5) last question:

Is there the best scheme among the above schemes?If not the best, what are the advantages and disadvantages of each

PS:

make complaints about the date of Java processing by the Tucao. The real TMD is a slag. It’s hard to use… This shell’s date command should be the best in user experience ~

REF:

[1] The beauty of Java programming

https://mp.weixin.qq.com/s/AijZmBzE8Yv0XqYfZQ4URg

[2] Static variables, instance variables and local variables of java thread safety

http://my.oschina.net/leejun2005/blog/130043

Similar Posts: