SimpleDateFormat - 不安全,但为什么呢?

时间:2016-04-22 13:21:48

标签: java multithreading date thread-safety simpledateformat

我最近意识到/了解到SimpleDateFormat存在一些严重问题,因为Java 8不应再使用了。我的意思是......我有点知道但从未对此给予过多关注。到现在为止还挺好。

但是好的......我在过去的7到8年里编写了很多遗留代码,使用SimpleDateFormat,将许多SimpleDateFormat个对象存储为静态字段,并使用它们来解析/格式日期。事实上,我从未在生产中(这些年来)生成这些SimpleDateFormat个实例(静态或非静态)的任何问题。

所以......我现在想要查看和分析这些遗留代码,看看其中是否存在SimpleDateFormat的任何危险用法。

因此我的问题是......

在什么情况下使用SimpleDateFormat确实有问题?
我可以获得某种清单,以便查看我的旧代码,看看我的任何方案是否都在“尽量避免”列表中?

3 个答案:

答案 0 :(得分:3)

SimpleDateFormat不是线程安全的,任何将它放在多个线程访问的字段中的情况都将是一个潜在的问题。它不会爆炸,但它可能导致生成不正确的结果。

对此进行分类的情况是检查您使用结果的内容以及这些值始终正确的重要性。显然,您遇到格式化程序的线程越多,出错的可能性就越大,因此多线程但很少有多个用户的应用程序可能会优先向下。

答案 1 :(得分:2)

例如,一个问题是SimpleDateFormat的内部字段设置为您要格式化的日期/日历。

因此,如果您有两个线程同时使用相同的SDF和两个不同的日期,则格式化的日期可能会在格式化过程中正确更改,从而导致字符串在两个日期之间混合。

这就是下面的例子模拟的内容。如果使用ExecutorService es = Executors.newFixedThreadPool(1);(单线程)运行它,则结果集只有两个日期,如预期的那样。如果您使用ExecutorService es = Executors.newFixedThreadPool(10);代替(多线程),结果集可能会有更多日期,这两个日期是两个日期的混合。

例如,在我的机器上输出为:

  • 单线程(预期结果):

      

    [02-Jan-1970 04:46:40,01-Jan-1970 01:00:00]

  • 多线程:

      

    [02-Jan-1970 01:00:00,11-Jan-1970 04:00:00,02-Jan-1970 01:00:40,11-Jan-1970 01:00:40,22- Jan-1970 04:46:00,02-Jan-1970 01:46:00,11-Jan-1970 01:00:00,02-Jan-1970 04:00:00,02-Jan-1970 01:46 :40,02-Jan-1970 04:00:40,01-Jan-1970 01:46:00,11-Jan-1970 01:46:40,02-Jan-1970 04:46:40,01-Jan -1970 04:00:40,11-Jan-1970 04:46:40,01-Jan-1970 04:46:00]

private static final DateFormat FMT = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
private static final CountDownLatch LATCH = new CountDownLatch(1);

public static void main(String[] args) throws Exception {
  ExecutorService es = Executors.newFixedThreadPool(1);
  Date d1 = new Date(0);
  Date d2 = new Date(100_000_000);
  List<Future<String>> futures = new ArrayList<> ();
  for (int i = 0; i < 10_000; i++) {
    Date d = i % 2 == 0 ? d1 : d2;
    Future<String> f = es.submit(() -> run(d));
    futures.add(f);
  }
  LATCH.countDown();
  es.shutdown();
  es.awaitTermination(5, TimeUnit.SECONDS);
  Set<String> results = new HashSet<> ();
  for (Future<String> f : futures) {
    results.add(f.get());
  }

  System.out.println(results);
}

private static String run(Date d) throws InterruptedException {
  LATCH.await();
  return FMT.format(d);
}

答案 2 :(得分:1)

SimpleDateFormat不是线程安全的,因此在每个多线程场景(例如Web应用程序)中,您不能在常量类中声明一个格式化程序并在您的业务方法中使用它而不存在奇怪错误的风险......特别是当你开始玩时区等时。