2016-04-22 12 views
3

私は最近、SimpleDateFormatに深刻な問題があり、Java 8はもう使用しないことを認識しました。私はそれを知っていたことは知っていましたが、それにはあまり注意を払わなかったのです。ここまでは順調ですね。SimpleDateFormat - 安全ではありませんが、正確にはなぜですか?

しかし、私は最後の7〜8年で書かれた多くのレガシーコードを持っています。SimpleDateFormatを使用し、多くのSimpleDateFormatオブジェクトを静的フィールドとして保存し、それらを使用して日付を解析/書式設定します。そして、実際には、これらの製品(これらすべての年代)のこれらのSimpleDateFormatインスタンス(静的かどうか)に問題は一度もありませんでした。

だから私は今このレガシーコードを見直して分析し、実際にSimpleDateFormatの危険な用途があるかどうか確認したいと思います。

したがって、私の質問は...何のシナリオの下で

は正確にSimpleDateFormat使用に問題があるのですか?
何らかの種類のチェックリストを取得して、私の古いコードを見直し、私のシナリオが「回避しようとする」リストに入っているかどうかを確認できますか?

+1

そう、スレッドセーフではありません2つのスレッドが同じ 'SimpleDateFormat'オブジェクトを同時に使用している場合には、問題が発生します。 – Henry

+0

@Henryはい、私はそう聞いてきました...しかし、OK ...それだけの問題ですか? –

+1

コードが現在動作している場合、突然新しい欠陥が追加されることはありません。しかし、新しいDate&Time APIは**正しく**使いやすくなりました。 –

答えて

1

SimpleDateFormatはスレッドセーフではありませんので、各マルチスレッドシナリオ(Webアプリケーションなど)では、定数クラスで1つのフォーマッタを宣言して、奇妙なバグの危険なしにビジネスメソッドを使用できます。タイムゾーンなどで再生を開始します。

3

SimpleDateFormatはスレッドセーフではありません。複数のスレッドがアクセスするフィールドに配置するシナリオは、潜在的な問題になります。それはあなたに爆発することはありませんが、誤った結果が生成される可能性があります。

これをトリアージングすると、結果を使用しているものと、それらの値が常に正しいことがどれだけ重要であるかをチェックすることになります。また、フォーマッタを叩いたスレッドが多いほど、エラーの可能性が高くなるため、マルチスレッドではないが、複数のユーザを持つことはめったにないアプリケーションが優先順位を下げる可能性があります。

2

たとえば、1つの問題は、SimpleDateFormatに書式設定する日付/カレンダーに設定された内部フィールドがあることです。

同じSDFを2つの異なる日付で同時に使用する2つのスレッドがある場合、フォーマット中の日付が書式の途中で変更され、結果として2つの日付のミックスである文字列になります。

これは、以下の例がシミュレートするものです。 ExecutorService es = Executors.newFixedThreadPool(1);(シングルスレッド)で実行すると、結果セットには2つの日付しか含まれません。代わりにExecutorService es = Executors.newFixedThreadPool(10);(マルチスレッド)を使用すると、結果のセットに2つの日付が混在するより多くの日付が含まれる可能性があります。

例えば、私のマシン上で出力である:

  • シングルスレッド(期待された結果):

    [02-JAN-1970年4時46分40秒、01-Jan- 1970一時00分00秒]

  • マルチスレッド:

    [01-Jan-1970 01:00:00、01-Jan-1970 04:00:00、01-Jan-1970 01:00:40、01-Jan-1970 01:00:40、02-Jan -1970 04:46:00、01-Jan-1970 01:46:00、01-Jan-1970 01:00:00、1970年2月1日04:00:00、1970年2月1日01:46: 40、02-Jan-1970 04:00:40、01-Jan-1970 01:46:00、01-Jan-1970 01:46:40、1970年2月1日04:46:40、01-Jan- 1970年4時00分40秒、01-JAN-1970午前四時46分40秒、01-JAN-1970午前4時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); 
}