SimpleDateFormat线程安全性测试与解决方案

Date formats are not synchronized.
It is recommended to create separate format instances for each thread.
If multiple threads access a format concurrently, it must be synchronized externally.
SimpleDateFormat不是一个线程安全的类,在多线程环境中调用相关方法,将会引发线程安全问题,本节演示其线程安全问题,并通过ThreadLocal解决其线程安全问题!
线程安全问题的演示:
package simpledateformat;

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


public class SimpleDateFormatTask implements Runnable {

    private ThreadLocal<SimpleDateFormat> threadLocal;
    private SimpleDateFormat simpleDateFormat;
    private String dateString;

    public SimpleDateFormatTask(SimpleDateFormat simpleDateFormat, String dateString) {
        this.simpleDateFormat = simpleDateFormat;
        this.dateString = dateString;
    }

    public SimpleDateFormatTask(ThreadLocal<SimpleDateFormat> threadLocal, String dateString) {
        this.threadLocal = threadLocal;
        this.dateString = dateString;
    }

    @Override
    public void run() {
        try {
            if (simpleDateFormat == null) {
                simpleDateFormat = threadLocal.get();
            }
            Date date = simpleDateFormat.parse(dateString);
            String newDate = simpleDateFormat.format(date);
            if (!newDate.equals(dateString)) {
                System.out.println("An error occurred in SimpleDateFormat!");
            }
        } catch (Exception e) {
            System.out.println("An exception occurred in SimpleDateFormat!");
        }
    }
}

class Main {
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

    public static void main(String[] args) {
        String[] dateStringArray = new String[]{"2021-02-01", "2021-02-02", "2021-02-03", "2021-02-04", "2021-02-05"};
        Thread[] threads = new Thread[dateStringArray.length];

        for (int i = 0; i < dateStringArray.length; i++) {
            threads[i] = new Thread(new SimpleDateFormatTask(simpleDateFormat,dateStringArray[i]));
            //threads[i] = new Thread(new SimpleDateFormatTask(threadLocal, dateStringArray[i]));
        }

        for (int i = 0; i < dateStringArray.length; i++) {
            threads[i].start();
        }
    }
}

通过ThreadLocal解决其线程安全问题:

package simpledateformat;

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


public class SimpleDateFormatTask implements Runnable {

    private ThreadLocal<SimpleDateFormat> threadLocal;
    private SimpleDateFormat simpleDateFormat;
    private String dateString;

    public SimpleDateFormatTask(SimpleDateFormat simpleDateFormat, String dateString) {
        this.simpleDateFormat = simpleDateFormat;
        this.dateString = dateString;
    }

    public SimpleDateFormatTask(ThreadLocal<SimpleDateFormat> threadLocal, String dateString) {
        this.threadLocal = threadLocal;
        this.dateString = dateString;
    }

    @Override
    public void run() {
        try {
            if (simpleDateFormat == null) {
                simpleDateFormat = threadLocal.get();
            }
            Date date = simpleDateFormat.parse(dateString);
            String newDate = simpleDateFormat.format(date);
            if (!newDate.equals(dateString)) {
                System.out.println("An error occurred in SimpleDateFormat!");
            }
        } catch (Exception e) {
            System.out.println("An exception occurred in SimpleDateFormat!");
        }
    }
}

class Main0 {
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

    public static void main(String[] args) {
        String[] dateStringArray = new String[]{"2021-02-01", "2021-02-02", "2021-02-03", "2021-02-04", "2021-02-05"};
        Thread[] threads = new Thread[dateStringArray.length];

        for (int i = 0; i < dateStringArray.length; i++) {
            //threads[i] = new Thread(new SimpleDateFormatTask(simpleDateFormat,dateStringArray[i]));
            threads[i] = new Thread(new SimpleDateFormatTask(threadLocal, dateStringArray[i]));
        }

        for (int i = 0; i < dateStringArray.length; i++) {
            threads[i].start();
        }
    }
}

补充:

ThreadLocal类提供线程局部变量。 这些变量与普通变量不同,每个线程访问该变量时候都有其自己的独立初始化的变量副本。

public class ThreadLocal<T> {
  
    protected T initialValue() {
        return null;
    }
   
    ...    
    
}

initialValue()方法返回此线程局部变量的当前线程的“初始值”。

除非线程先前调用了set()方法,否则线程将在第一次使用get()方法访问该变量时调用此方法,在这种情况下,initialValue()方法将不会被调用。

通常,每个线程最多调用一次此方法,但是在随后调用remove()之后再调用get()方法的情况下,可以再次调用此方法。

默认实现仅返回null;。

如果程序员希望线程局部变量的初始值不是null,则必须将ThreadLocal子类化,并重写此方法。通常,将使用匿名内部类的方法子类化。

package simpledateformat;


public class ThreadLocalTest {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return new String("Hello,ThreadLocal!");
        }
    };

    public static void main(String[] args) {
        Thread thread1 = new Thread(()->{
            System.out.println(System.identityHashCode(threadLocal.get()));
            threadLocal.remove();
        });
        Thread thread2 = new Thread(()->{
            System.out.println(System.identityHashCode(threadLocal.get()));
            threadLocal.remove();
        });
        thread1.start();
        thread2.start();
    }
}

更多内容:https://www.cnblogs.com/iuyy/p/13518177.html

原文地址:https://www.cnblogs.com/iuyy/p/14379067.html