java.text.SimpleDateFormat はスレッドセーフではない

複数のスレッドからformat()やparse()を使うと、おかしな結果になることがあります。発生頻度が少ないので、長時間の連続運用テストなどを行わないと見つからない場合もあるというのが悩ましいところです。

SunのBug Paradeを、"+SimpleDateFormat +thread"で検索してもらうとすぐわかるように、SimpleDateFormatクラスは、複数のインスタンスで同じオブジェクトを共有して持っており、スレッドセーフではありません。

これらのバグレポートの解説を総合すると、スレッドセーフにするには、

のいずれかしかないという結論になります。

上の処置を行わない場合の影響としては、

といった現象があるようです

自分でSimpleDateFormatを一切使わなくても、new Date().toString()などを行っていれば同じことが起こりえます(内部でSimpleDateFormatを使っているため)。

StringIndexOutOfBoundsExceptions や NumberFormatException は、RuntimeException なので、対応するcatchが置かれていない可能性もあります(Exceptionではなく、例えば、IOExceptionだけをcatchするなど、他の例外が起こることを想定してない箇所)。 RuntimeExceptionやErrorをちゃんと拾っていない作りだと、Javaアプリの実行者(ユーザ)にこれが見えて、止まってしまうこともあるでしょう。

実験方法

以下のソースを実行してください。シングルスレッドでは起こりえないはずの条件文が、trueになったり、前記の例外が発生したりします。

関連情報

NumberFormatなど、他のFormatのサブクラスも、元々スレッドセーフではないんだけど、NumberFormat#format()は直すの簡単だったからスレッドセーフにしたよ。と、なんだかいいかげんにも見える理由で、NumberFormat#format()は大丈夫らしい。(NumberFormat#parse()の方はだめらしい)
#4101500

Core APIのスレッドセーフ性について

Bug Paradeのcommentsでよく不満が述べられていることだが、Javaのドキュメントに「そのクラスがスレッドセーフか否か」が書かれていないことが一番の問題です。

マルチスレッド環境下で使おうとするクラスを、いちいちソースを読んで自分でthread-safenessを確認しなければならないというのは、現実的とは思えませんが、さてどうしたものか。

他のログを取るソフトウェアはどうしているのか

SimpleDateFormatがスレッドセーフでないことを、世の有名ソフトウェアはどう回避しているのか、オープンソースのプロダクトを調査してみました

調査対象は、以下の観点で選択しました。

log4j

java.text.SimpleDateFormat より柔軟性はないが、速度を重視した4つのDateFormatクラスを独自に作成し、それを使ってログを出している(http://www.log4j.org/log4j/HISTORY Jan 29, 2000より)ということです。ここのところの工夫は、バグ問題は別としても参考にすべきものがあるでしょう。

結果として、SimpleDateFormatを使っていないので、問題は起こらないということになります。

開発者用MLのアーカイブも検索したが、DateFormatとスレッドに絡む話は出ていないようです。

Jigsaw

リリースノートの過去のバグには、DateとThreadに関する項目はありませんでした

MLのアーカイブを検索したが、それらしいものはありませんでした。

Tomcat

バグ追跡システムには、DateとThreadに関する情報なし

開発者アーカイブ中に、「SimpleDateFormatは有害?」というスレッド発見

ただし、スレッドアンセーフに関する話ではなく、ストレステスト時のパフォーマンスを問題にして、改善のためのより高速なDateFormatを提案しているもののようです。(log4jと同様)

もし、これらで提案/利用されている独自のDateFormatクラスが、

なら、こういったものを流用する/参考にすることには意味があると考えます。(車輪を2度発明しない)

log4jやtomcatで使っているDateFormatクラスが、ロケールセンシティブかどうかは調べていないが、ログ出力の際の日付は、USロケール(英語)決め打ちの場合がむしろ多いのではないか。

連絡先

間違いの指摘、追加情報、関連情報(他のCore APIでスレッドアンセーフなものとか)などありましたらこちらへお願いします
akky@bigfoot.com