一、简介
SimpleDateFormat类内部有一个Calendar对象引用,如下图:
SimpleDateFormat类的parse方法中,使用了Calendar的clear方法等,如下图:
当我们编写DateUtil工具类时,可能会将SimpleDateFormat设置为static的,那么在并发的情况下线程共享同一个calendar,可能会出现问题。
二、解决办法
①去掉static,这样每次用到SimpleDateFormat的时候都会新建一个,例如四个线程执行1000个需要用到SimpleDateFormat的任务,那么将会有1000次SimpleDateFormat的创建和销毁,比较耗费资源。这里不详述。
②使用ThreadLocal,这样每个线程只会创建一次SimpleDateFormat。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhenwei.liu created on 2013 13-8-29 下午5:35
* @version $Id$
*/
public class DateUtil {
/** 锁对象 */
private static final Object lockObj = new Object();
/** 存放不同的日期模板格式的sdf的Map */
private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
/**
* 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
*
* @param pattern
* @return
*/
private static SimpleDateFormat getSdf(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
// 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
if (tl == null) {
synchronized (lockObj) {
tl = sdfMap.get(pattern);
if (tl == null) {
// 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
System.out.println("put new sdf of pattern " + pattern + " to map");
// 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
tl = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern, tl);
}
}
}
return tl.get();
}
/**
* 是用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
*
* @param date
* @param pattern
* @return
*/
public static String format(Date date, String pattern) {
return getSdf(pattern).format(date);
}
public static Date parse(String dateStr, String pattern) throws ParseException {
return getSdf(pattern).parse(dateStr);
}
}
类似双重检验的单例模式,不难理解。
需要强调的是这里
这里不仅仅是简单的创建了ThreadLocal<SimpleDateFormat>类,还重写了ThreadLocal类的InitialValue方法。
重写的作用是什么呢?当线程使用该ThreadLocal的get()方法时,若获取的为空,那么就会执行InitialValue方法初始化,这时将会初始化创建一个SimpleDateFormat类,并存放在线程类的ThreadLocalMap中。
ThreadLocal的get()方法源码如下:
我们重写了initialValue()方法,返回一个SimpleDateFormat类。
也可以预先定义一些常用的日期格式,使用static块预先定义。
package com.example.test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhenwei.liu created on 2013 13-8-29 下午5:35
* @version $Id$
*/
public class DateUtil3 {
/** 锁对象 */
private static final Object lockObj = new Object();
/** 存放不同的日期模板格式的sdf的Map */
private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
static{
sdfMap.put("yyyy-MM-dd HH:mm:ss",new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
});
}
/**
* 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
*
* @param pattern
* @return
*/
private static SimpleDateFormat getSdf(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
// 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
if (tl == null) {
synchronized (lockObj) {
tl = sdfMap.get(pattern);
if (tl == null) {
// 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
System.out.println("put new sdf of pattern " + pattern + " to map");
// 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
tl = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern, tl);
}
}
}
return tl.get();
}
/**
* 是用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
*
* @param date
* @param pattern
* @return
*/
public static String format(Date date, String pattern) {
return getSdf(pattern).format(date);
}
public static Date parse(String dateStr, String pattern) throws ParseException {
Date parse = getSdf(pattern).parse(dateStr);
System.out.println(Thread.currentThread()+",parse:"+parse);
return parse;
}
}
学习借鉴自
https://www.cnblogs.com/zemliu/archive/2013/08/29/3290585.html