• 避坑!SimpleDateFormat不光线程不安全,还有这个隐患


    众所周知,SimpleDateFormat是多线程不安全的

    下面这段代码通过多线程使用同一个SimpleDateFormat对象的parse方法, 多次执行代码来测试,可以看到会出现两种预想不到的现象----->要么出现不正确的时间解析结果,要么抛出message各异的NumberFormatException异常。 @see>>借助SimpleDateFormat来谈谈java里的多线程不安全

    package jstudy.dateformat;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class SimpleDateFormatTest {
        public static void main(String[] args) throws ParseException, InterruptedException {
            ExecutorService threadPool = Executors.newFixedThreadPool(20);
            SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String startDate = "2022-01-07 15:40:15";
            for (int i = 0; i < 20; i++) {
                threadPool.execute(() -> {
                    for (int j = 0; j < 20; j++) {
                        try {
                            // new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(startDate); //使用局部变量可以避免出现线程不安全
                            System.out.println(datetimeFormat.parse(startDate));
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }

    SimpleDateFormat不光线程不安全,还有这个隐患

    如下代码运行结果是什么?

        @Test
        public void test() throws ParseException {
            SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMddHHmmss");//pattern
            System.out.println( simpleDateFormat.parse("2022-09-30 13:53:14"));
        }

    答案:

    Wed Dec 08 21:00:13 CST 2021


    可见,字符串时间与指定的pattern不匹配,导致出现意外结果。

    而我们这次在现网排查问题时,正好是遇到了这个坑。


    数据库里的付款记录,竟然存在付款完成时间比记录创建时间还早的付款单,而且还早了多半年。

    我们可以确定的是create_time赋值是没问题的。那看来就是payment_finish_time的赋值有问题。

    赶紧扒代码,在三方付款服务商的service里,发现如下语句:

    //完成时间
    payResultVo.setOrderFinishTime(DateUtils.str2Date(qi.getEndTime(),DateUtils.datetimeFormat));

    其中,DateUtils 存在于公司公共技术common包,封装了时间相关处理操作,DateUtils#str2Date 是将特定格式的时间字符串转换成Date对象。DateUtils.datetimeFormat 是 public static final SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    瞬间想到,必然是SimpleDateFormat线程不安全导致的。为了快速解决问题,修改DateUtils#str2Date 的实现:

     1 public static Date str2Date(String str, SimpleDateFormat sdf) {
     2     if (null == str || "".equals(str)) {
     3         return null;
     4     }
     5     try {
     6         return sdf.parse(str);
     7     } catch (ParseException e) {
     8         e.printStackTrace();
     9     }
    10     return null;
    11 }

    ➽ 

    ➽ 

     1 public static Date str2Date(String str, SimpleDateFormat sdf) {
     2     if (null == str || "".equals(str)) {
     3         return null;
     4     }
     5     try {
     6         SimpleDateFormat actualSdf = new SimpleDateFormat(sdf.toPattern());
     7         return actualSdf.parse(str);
     8     } catch (ParseException e) {
     9         e.printStackTrace();
    10     }
    11     return null;
    12 }

    后来竟然发现这个改动没起作用!就是说,依然存在付款完成时间早于创建时间的付款单。这就不科学了~

    排查原因着实费了几番周折。

    再后来,才真相大白。原来,服务商响应报文里的时间格式不是 yyyy-MM-dd HH:mm:ss,而是 yyyyMMddHHmmss 。由于程序员当时的粗心,直接拷贝别的对接程序里的代码,再加上测试没有覆盖到这个case,导致运行了一个月,才发现这个问题。

    赶紧加上个必要的代码注释,给“伸手党”式的粗心同学提个醒。

  • 相关阅读:
    正敲着代码,鼠标坏了!
    DB2 OLAP函数的使用(转)
    修剪矩形
    classpath和环境变量设置(转)
    MyEclipse断点调试JavaScript浅析(转)
    Onunload和onbeforeunload方法的异同
    db2中的coalesce函数(转)
    db2:根据TABLEID找table
    [转]DB2行列转换
    DB2删除数据时的小技巧
  • 原文地址:https://www.cnblogs.com/buguge/p/16744992.html
Copyright © 2020-2023  润新知