SimpleDateFormat 線程不安全坑

 SimpleDateFormat 使用 public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat();全局變量的方式來創建,將會出現線程不安全,請在方法內部重新new,出現線程安全問題,很難排查,開始的時候時間不對感覺可能是線程安全問題,使用線程池測試了好多次也沒發現,今天又重新測試發現了問題(測試方法問題)。

DateTimeFormatter 是線程安全的,可以配合 LocalDateTime 使用,計算時間增加或減少非常方便,代碼量也少。

 




import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class T{
	
	public List<String> getList(){
				
		List<String> list = new ArrayList();
		list.add("2020-04-06 10:11:08");
		list.add("2020-04-07 11:12:09");
		list.add("2020-04-08 12:13:10");
		list.add("2020-04-09 13:14:11");
		list.add("2020-04-10 14:15:23");
		list.add("2020-04-03 15:10:03");
		list.add("2020-04-01 01:06:53");
		list.add("2020-12-05 09:21:23");
		list.add("2020-04-04 07:36:18");
		list.add("2020-04-11 21:45:13");
		return list;
	}
	
	private int getNum(int num) {
        return (int) (Math.random() * num) + 1;
    }
	
	//普通線程測試
	public void TestThread(){
		List<String> list = getList();
        for(int i = 0 ; i < list.size() ; i++){
            int finalI = i;
            new Thread(() -> {
                String sTime;
                Calendar cal = Calendar.getInstance();
                cal.setTime(StrUtil.stringToDate(list.get(finalI)));
                cal.add(Calendar.HOUR_OF_DAY, -1);
                sTime = StrUtil.getCurStringDate(cal.getTime(), StrUtil.PATTERN);
                System.out.println("SDF原時間:" + list.get(finalI) + " -> 計算後:" + sTime);
								
				LocalDateTime startTime =  LocalDateTime.parse( list.get(finalI),StrUtil.dtf);
				LocalDateTime newTime = startTime.plusHours(-1);
				System.out.println("原時間:" + list.get(finalI) + " -> 計算後:" + newTime.format(StrUtil.dtf));
            }).start();		
        }
	}
	
	//線程池測試
	public void TestThreadPool(){
		ExecutorService executor = Executors.newFixedThreadPool(10);
		List<String> list = getList();
        for(int i = 0 ; i < list.size() ; i++){
			int finalI = i;
			executor.submit(()->{
				String sTime;
                Calendar cal = Calendar.getInstance();
                cal.setTime(StrUtil.stringToDate(list.get(finalI)));
                cal.add(Calendar.HOUR_OF_DAY, -1);
                sTime = StrUtil.getCurStringDate(cal.getTime(), StrUtil.PATTERN);
                System.out.println("SDF原時間:" + list.get(finalI) + " -> 計算後:" + sTime);
								
				LocalDateTime startTime =  LocalDateTime.parse( list.get(finalI),StrUtil.dtf);
				LocalDateTime newTime = startTime.plusHours(-1);
				System.out.println("原時間:" + list.get(finalI) + " -> 計算後:" + newTime.format(StrUtil.dtf));		
			});
        }
	}

    public static void main(String[] args) {
		T t  = new T();
		t.TestThread();
		t.TestThreadPool();
    }
}

class StrUtil {
	
	public static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");//線程安全
    public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat();//線程不安全
    public static final String PATTERN = "yyyy-MM-dd HH:mm:ss";


    /**
     * 獲取指定時間的時間字符串,在多線程中此方法將不安全(如何必須要使用,請在方法內部new不要使用全局變量)
     */
    public static String getCurStringDate(Date date, String pattern) {
        DATE_FORMAT.applyPattern(pattern);
        return DATE_FORMAT.format(date);
    }

    /**
     * 字符串轉日期對象
     */
    public static Date stringToDate(String str) {

        SimpleDateFormat format;
        if (str.length() == 16) {
            format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        } else if (str.length() == 10) {
            format = new SimpleDateFormat("yyyy-MM-dd");
        } else {
            format = new SimpleDateFormat(StrUtil.PATTERN);
        }
        try {
            return format.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章