SimpleDateFormatのスレッドセキュリティの問題

1975 ワード

Javaプログラマーの皆さんは、SimpleDateFormatに慣れていないと思います.しかし、SimpleDateFormatはスレッドが安全ではないことを知っていますか(thread safe).これは、次のコードが間違っていることを意味します.

class Sample {
  private static final DateFormat format = new SimpleDateFormat("yyyy.MM.dd");

  public String getCurrentDateText() {
    return format.format(new Date());
  }
}

機能の観点から、このコードを単独で実行するのは問題ありませんが、SimpleDateFormatはスレッドが安全ではないため、このコードはエラーになります.このコードを正しくするには、少し微調整します.

public class Sample {
    public String getCurrentDateText() {
        return new SimpleDateFormat("yyyy.MM.dd").format(new Date());
    }
}

ここでの調整は元の共有formatという変数だけで、このメソッドを呼び出すたびに新しいSimpleDateFormat変数が作成されることに気づいたかどうか分かりません.
プロフェッショナルプログラマーとして、変数を共有するコストが作成するたびに小さくなることは当然知っています.私たちがそうしなければならないのは、SimpleDateFormatがスレッドで安全ではないからです.しかし、SimpleDateFormatが提供してくれたインタフェースから見ると、スレッドのセキュリティと関係があるとは思えません.それでは、JDKのソースコードを開けて、その中のコードの醜さを見てみましょう.
JDKのソースコードが手元にない場合は、ここがいい参考になります.
formatメソッドには、次のコードがあります.

calendar.setTime(date);

ここでcalendarはDateFormatのprotectedフィールドです.この文はcalendarを変更し、後でcalendarは(subFormatメソッドで)使用しますが、これが問題の根源です.
1つのマルチスレッド環境で、同じSimpleDateFormatのインスタンスを持つ2つのスレッドがformatメソッドをそれぞれ呼び出していることを想像してみてください.
スレッド1はformatメソッドを呼び出し、calendarというフィールドを変更します.
中断した.
スレッド2は実行を開始し、calendarも変更されました.
また途切れた.
スレッド1が戻ってきたとき、calendarはすでに設定された値ではなく、スレッド2設計の道を歩んでいた.
BANG!!! formatの実装を少し時間をかけて分析すると、calendarを使用する唯一のメリットは、subFormatを呼び出すときにパラメータが1つ足りないのに、多くの問題をもたらすことです.実は、ここで局所変数を使って、一緒に伝えれば、すべての問題が解決されます.
この問題の背後にはもっと重要な問題が隠されている:無状態.
ステータスレスメソッドの利点の1つは、さまざまな環境で安全に呼び出すことができることです.メソッドがステータスであるかどうかを測定するには、インスタンスのフィールドなどのグローバル変数などの他のものを変更したかどうかを見ます.formatメソッドは実行中にSimpleDateFormatのcalendarフィールドを変更したので、ステータスがあります.
プログラムを書くには、できるだけ無状態の方法を書きます.
FROM: http://www.infoq.com/cn/news/2012/06/ugly-code-12