在需要进行日期与字符串相互转换的类中,经常会声明一个静态的SimpleDateFormat变量,如下所示:1
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
然后就可以直接使用sdf的format和parse方法进行日期与字符串的相互转换,但是SimpleDateFormat并不是线程安全的,我们使用如下代码验证SimpleDateFormat的线程安全问题:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26public class Test {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static String[] dates = new String[]{"2015-07-01 10:45:52", "2015-07-02 09:34:11",
"2015-07-01 07:12:32", "2015-07-03 15:06:54", "2015-07-02 21:12:38",
"2015-07-01 01:59:01", "2015-07-02 13:51:18", "2015-07-03 10:05:08"};
public static class MyThread extends Thread {
public void run() {
for (int i=0; i<100; i++) {
try {
System.out.println(Thread.currentThread().getName() + "\t" + sdf.format(sdf.parse(dates[new Random().nextInt(8)])));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws ParseException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
MyThread线程循环100次,每次从dates数组中随机取出一个日期字符串,依次进行parse和format操作后再输出最终的日期字符串,若声明一个MyThread线程运行,则输出如图所示:
输出的日期字符串都在dates数组中,说明parse和format操作均正常。若声明两个MyThread线程运行,则输出如图所示:
输出的日期字符串有不在dates数组中的,且还存在异常,说明parse和format操作并不是线程安全的。
在多线程下若要使用SimpleDateFormat,一种方法是每次进行日期与字符串转换时,声明一个SimpleDateFormat变量,但这样会产生过多的变量实例,另一种方法是使用ThreadLocal为每个线程保存一个SimpleDateFormat变量实例,如下所示:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class DateUtil {
private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
protected synchronized SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static SimpleDateFormat get() {
return threadLocal.get();
}
public static Date parse(String s) throws ParseException {
return get().parse(s);
}
public static String format(Date d) {
return get().format(d);
}
}
同时修改MyThread,使用DateUtil进行日期与字符串转换,如下所示:1
2
3
4
5
6
7
8
9
10
11public static class MyThread extends Thread {
public void run() {
for (int i=0; i<100; i++) {
try {
System.out.println(Thread.currentThread().getName() + "\t" + DateUtil.format(DateUtil.parse(dates[new Random().nextInt(8)])));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
再声明两个MyThread线程运行,则输出正常。