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;
    }
}

 

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