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:
0import 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 usedHere, 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