【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类。
怎么理解,这里记录下。
SimpleDateFormat 是 Java 中一个非常常用的类,该类用来对日期字符串进行解析和格式化输出,
但如果使用不小心 会导致非常微妙和难以调试的问题,
因为 DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下 调用 format() 和 parse() 方法应该使用同步代码来避免问题。
通过一个具体的场景来深入理解SimpleDateFormat 类。
一.引子
在程序中我们应当尽量少的创建SimpleDateFormat 实例,因为创建这么一个实例需要耗费很大的代价。
在一个读取数据库数据导出到excel文件的例子当中,每次处理一个时间信息的时候,就需要创建一个SimpleDateFormat实例对
象,然后再丢弃这个对象。
大量的对象就这样被创建出来,占用大量的内存和 jvm空间。
代码如下:
package com.peidasoft.dateformat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static String formatDate(Date date)throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(strDate);
}
}
也许会说,OK,那我就创建一个静态的simpleDateFormat实例,然后放到一个DateUtil类(如下) 中,在使用时
直接使用这个实例进行操作,这样问题就解决了。
改进后的代码如下:
package com.peidasoft.dateformat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date)throws ParseException{
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException{
return sdf.parse(strDate);
}
}
当然,这个方法的确很不错,在大部分的时间里面都会工作得很好。
但当你在生产环境中使用一段时间之后,你就会发现这么一个事实:它不是线程安全的。
在正常的测试情况之下,都没有问题,但一旦在生产环境中一定负载情况下时,这个问题就出来了。
他会出现各种不同的情况,比如转化的时间不正确,比如报错,比如线程被挂死等等。我们看下面
的测试用例,那事实说话:
package com.peidasoft.dateformat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date)throws ParseException{
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException{
return sdf.parse(strDate);
}
}
package com.peidasoft.dateformat;
import java.text.ParseException;
import java.util.Date;
public class DateUtilTest {
public static class TestSimpleDateFormatThreadSafe extends Thread {
@Override
public void run() {
while(true) {
try {
this.join(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
System.out.println(this.getName()+":"+DateUtil.parse("2013-05-2 4 06:02:20"));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
for(int i = 0; i < 3; i++){
new TestSimpleDateFormatThreadSafe().start();
}
}
执行输出如下:
Exception in thread "Thread-1" java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082)
at java.lang.Double.parseDouble(Double.java:510)
at java.text.DigitList.getDouble(DigitList.java:151)
at java.text.DecimalFormat.parse(DecimalFormat.java:1302)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
at java.text.DateFormat.parse(DateFormat.java:335)
at com.peidasoft.orm.dateformat.DateNoStaticUtil.parse(DateNoStaticUtil.java:17)
at com.peidasoft.orm. dateformat.DateUtilTest$TestSimpleDateFormatThreadSafe.
run(DateUtilTest.java:20)
Exception in thread "Thread-0" java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082)
at java.lang.Double.parseDouble(Double.java:510)
at java.text.DigitList.getDouble(DigitList.java:151)
at java.text.DecimalFormat.parse(DecimalFormat.java:1302)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
at java.text.DateFormat.parse(DateFormat.java:335)
at com.peidasoft.orm.dateformat.DateNoStaticUtil.parse(DateNoStaticUtil.java:17)
at com.peidasoft.orm.dateformat.DateUtilTest$TestSimpleDateFormatThreadSafe.run(DateUtilTest.java:20)
Thread-2:Mon May:02:20 CST 2021
Thread-2:Fri May 24 06:02:20 CST 2013
Thread-2:Fri May 24 06:02:20 CST 2013
Thread-2:Fri May