SimpleDateFormat线程不安全问题

首先简单说一下SimpleDateFormat存在线程安全问题的原因。

SimpleDateFormat继承了DateFormat类,类中有一个受保护类型的Calendar对象,看一下SimpleDateFormat的format方法:

// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
                           FieldDelegate delegate) {
  // Convert input date to time field list 
// 问题就出在这里
   calendar.setTime(date);
  ......
   }
   return toAppendTo;

多个线程共享SimpleDateFormat实例时,对Calendar对象的更改会相互影响,因此产生线程安全问题。

解决方案:
1、每当使用时创建一个SimpleDateFormat的实例,但性能会较差(其实以现在jdk的优异表现和软硬件性能的提升,
创建SimpleDateFormat实例对性能的影响并不是很明显)
2、使用ThreadLocal避免多线程共享实例。(推荐)
实现如下

import org.apache.commons.collections.map.LRUMap;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;

public class DateFormatUtil {

    private static final int MAX_SDF_SIZE = 20;
// 不一定要LRUMap,任意Map都行
    private static final ThreadLocal<LRUMap> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<LRUMap>() {
        @Override
        protected LRUMap initialValue() {
            return new LRUMap(MAX_SDF_SIZE);
        }
    };

    public static SimpleDateFormat getDateFormat(String pattern) {
        LRUMap sdfMap = DATE_FORMAT_THREAD_LOCAL.get();
        SimpleDateFormat sdf = (SimpleDateFormat) sdfMap.get(pattern);
        if (sdf == null) {
            sdf = new SimpleDateFormat(pattern);
            sdf.setLenient(false);
            sdfMap.put(pattern, sdf);
        }
        return sdf;
    }

    public static void destory() {
        DATE_FORMAT_THREAD_LOCAL.remove();
    }

    public static Date parse4y2M2d(String source) {
        try {
            return getDateFormat("yyyy-MM-dd").parse(source);
        } catch (Exception e) {
            return null;
        }
    }

    public static Date parseyyyyMMdd(String source) {
        try {
            return getDateFormat("yyyyMMdd").parse(source);
        } catch (Exception e) {
            return null;
        }
    }

    public static Date parseyyyyMMddHHmmss(String source) {
        try {
            return getDateFormat("yyyyMMddHHmmss").parse(source);
        } catch (Exception e) {
            return null;
        }
    }


    public static Date parse4y2M2d2h2m2s(String source) {
        try {
            return getDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);
        } catch (Exception e) {
            return null;
        }
    }

    public static Date parse4y2M2d2h2m(String source) {
        try {
            return getDateFormat("yyyy-MM-dd HH:mm").parse(source);
        } catch (Exception e) {
            return null;
        }
    }

    public static Date parse4y2M2dT2h2m2s(String source) {
        try {
            return getDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(source);
        } catch (Exception e) {
            return null;
        }
    }

    public static Date parse4y2M2dhhmm(String source) {
        try {
            return getDateFormat("yyyy-MM-dd HHmm").parse(source);
        } catch (Exception e) {
            return null;
        }
    }


    public static String format4y2M2d(Date date) {
        if (Objects.isNull(date)){
            return null;
        }
        return getDateFormat("yyyy-MM-dd").format(date);
    }

    public static String format4y2M2dCN(Date date) {
        return getDateFormat("yyyy年MM月dd日").format(date);
    }

    public static String format2M2dCN(Date date) {
        return getDateFormat("MM月dd日").format(date);
    }

    public static String formatMdCN(Date date) {
        return getDateFormat("M月d日").format(date);
    }

    public static String formatyyyyMMdd(Date date) {
        return getDateFormat("yyyyMMdd").format(date);
    }

    public static String parseyyyyMMddHHmmss(Date date) {
        return getDateFormat("yyyyMMddHHmmss").format(date);
    }

    public static String format4y2M2d2h2m2s(Date date) {
        if (Objects.isNull(date)){
            return null;
        }
        return getDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }

    public static String formatyyyyMMddHHmmss(Date date) {
        return getDateFormat("yyyyMMddHHmmss").format(date);
    }

    public static String format4y2M2d2h2m(Date date) {
        return getDateFormat("yyyy-MM-dd HH:mm").format(date);
    }

    public static String format2h2m2s(Date date) {
        return getDateFormat("HH:mm:ss").format(date);
    }

    public static String format2h2m(Date date) {
        return getDateFormat("HH:mm").format(date);
    }

    public static String formathhmm(Date date) {
        return getDateFormat("HHmm").format(date);
    }

    private static Calendar getCalendar(String pattern) {
        LRUMap sdfMap = DATE_FORMAT_THREAD_LOCAL.get();
        if (sdfMap.containsKey(pattern)) {
            return (Calendar) sdfMap.get(pattern);
        } else {
            Calendar cal = Calendar.getInstance();
            sdfMap.put(pattern, cal);
            return cal;
        }
    }

    public static Date parseLong2Date(long longTime) {
        Calendar cal = getCalendar("long2Date");
        cal.clear();
        cal.setTimeInMillis(longTime);
        return cal.getTime();
    }

    public static Date parseHHmm(String source) {
        try {
            return getDateFormat("HH:mm").parse(source);
        } catch (Exception e) {
            return null;
        }
    }
}
# java 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×