SimpleDateFormat 讲解

【强制】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 24 06:
上一篇:Markdown相关


下一篇:搞懂gopath golang go go项目结构