preface

SimpleDateFormat is a thread-safe class. It is mandatory to use SimpleDateFormat as a thread-safe class.

SimpleDateFormat = SimpleDateFormat = SimpleDateFormat Java8 has been released for N years. Go to LocalDateTime. Today, let’s talk about thread safety in SimpleDateFormat.

SimpleDateFormat is not thread-safe

SimpleDateFormat = SimpleDateFormat = SimpleDateFormat = SimpleDateFormat = SimpleDateFormat; As follows:

public class Main {

    private final static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) throws ParseException {
        System.out.println(SDFT.parse("The 2019-05-29 12:12:12")); }}Copy the code

Of course, there is no problem running this code directly. However, when this SDFT instance is applied to multithreaded environments, fatal problems arise. Assume the following code:

public class Main {

    private static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd");

    public static void main(String[] args) {
        for (int i = 1; i < 31; i++) {
            int ii = i;
            new Thread(() -> {
                Date date = null;
                try {
                    String s = "2019-05 -" + ii;
                    date = SDFT.parse(s);
                    System.out.println("" + ii + ":" + date.getDate());
                } catch(ParseException e) { e.printStackTrace(); } }).start(); }}}Copy the code

Create 30 threads to convert different time strings, then print out the result:

(Running this code can also cause exceptions caused by thread-safety issues.)

According to the “expected result”, the numbers on both sides should be equal. Why is the output not equal here? DateFormat source code can be viewed:

Because SimpleDateFormat is defined to be shared, the calendar property in its class is also shared by multiple threads, creating thread-safety issues.

The solution

Scheme 1: lock processing

As in the example in this article, thread safety can be ensured by locking:

public class Main {

    private static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd");

    public static void main(String[] args) {
        for (int i = 1; i < 31; i++) {
            int ii = i;
            new Thread(() -> {
                Date date = null;
                try {
                    String s = "2019-05 -" + ii;
                    synchronized (Main.class) {
                        date = SDFT.parse(s);
                    }
                    System.out.println("" + ii + ":" + date.getDate());
                } catch(ParseException e) { e.printStackTrace(); } }).start(); }}}Copy the code

Output:

4:4
3:3
1:1
2:2
29:29
28:28
27:27
26:26
30:30
25:25
23:23
21:21
20:20
22:22
18:18
24:24
19:19
17:17
16:16
14:14
15:15
12:12
13:13
10:10
11:11
9:9
7:7
6:6
5:5
8:8
Copy the code

Scheme 2: Create an instance of SimpleDateFormat each time

The code modification is as follows:

    public static void main(String[] args) {
        for (int i = 1; i < 31; i++) {
            int ii = i;
            new Thread(() -> {
                Date date = null;
                try {
                    String s = "2019-05 -" + ii;
                    date = new SimpleDateFormat("yyyy-MM-dd").parse(s);
                    System.out.println("" + ii + ":" + date.getDate());
                } catch(ParseException e) { e.printStackTrace(); } }).start(); }}Copy the code

Every time SimpleDateFormat is used, create an instance of SimpleDateFormat to ensure that the SimpleDateFormat instance is not shared.

Solution 3: Use LocalThread

This is one of the solutions mentioned in the Ali Java specification. The reason why we can use LocalThread to solve this problem is as follows:

public class Main {

    private static final ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue(a) {
            return new SimpleDateFormat("yyyy-MM-dd"); }};public static void main(String[] args) {
        for (int i = 1; i < 31; i++) {
            int ii = i;
            new Thread(() -> {
                Date date = null;
                try {
                    String s = "2019-05 -" + ii;
                    date = threadLocal.get().parse(s);
                    System.out.println("" + ii + ":" + date.getDate());
                } catch(ParseException e) { e.printStackTrace(); } }).start(); }}}Copy the code

The running results are as follows:

22:22
2:2
24:24
15:15
17:17
16:16
29:29
9:9
30:30
3:3
4:4
5:5
12:12
8:8
20:20
26:26
21:21
28:28
19:19
27:27
18:18
1:1
14:14
25:25
11:11
13:13
7:7
6:6
23:23
10:10
Copy the code

Solution 4: Use the DateTimeFormatter provided by JDK1.8 to handle time.