- - PR -
SimpleDateFormat使用の際の同期化について
投稿者 | 投稿内容 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-05-31 18:59
こんにちは、jimと申します。
掲題の件について、どなたかご存知の方がいたら教えてください。 現在、とあるWebシステムを作っているのですが、 その過程で「SimpleDateFormat(その他複数のFormat系クラス)はスレッドセーフでないので、 同期処理をしなければならない」という情報を得ました。 私が作っているシステムでも複数のソースで同クラスを使用しており、 同じプロジェクトのメンバー曰く、下記のようにするべきと指示がありました。 synchronized( this ) { String strDate = "20050101"; SimpleDateFormat formatter = new SimpleDateFormat( "yyyyMMdd" ); Date date = formatter.parse( strDate ); } しかしながら、どうも解せないのでSimpleDateFormatのソースを確認したところ、 同クラスはクラス変数として、フォーマット形式("yyyyMMdd"等)を保持 しているように見受けられるので、上記の同期処理ではSimpleDateFormatクラスが クラス変数として持っているメンバへ各クラスからスレッドが同時にアクセスしてしまう ように思われます。 これを回避するには、特定のクラスに唯一のロックオブジェクトを定義し、 各クラスからは、このロックオブジェクトでsynchronizedしなければならないと 思われるのですが、上記のコードで正常な同期は図れるのでしょうか? | ||||||||||||||||
|
投稿日時: 2007-05-31 19:13
メソッド内であれば スレッドセーフ、クラス単位のであれば 場合により崩れる
| ||||||||||||||||
|
投稿日時: 2007-05-31 19:53
Google ツールバーでクラス名を入力しただけでも、スレッドセーフが候補が出るくらいですから、とても有名な問題のようです。 http://www.google.co.jp/search?hl=ja&ie=UTF-8&q=SimpleDateFormat+%e3%82%b9%e3%83%ac%e3%83%83%e3%83%89%e3%82%bb%e3%83%bc%e3%83%95 ちなみにここで言う「スレッドセーフ」とは、「ArrayList はスレッドセーフじゃないけど Vector はスレッドセーフです」というのとは意味が違い、より致命的なんですよね。この問題を指し示す、なにかもっと的確な用語ってあるのでしょうか?「スレッドセーフ」だけだと問題が区別しにくいですよね。
this がシステム内で唯一のものなら、たぶんそれで良いのではないでしょうか。 -- unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86} | ||||||||||||||||
|
投稿日時: 2007-05-31 22:53
引用:
-------------------------------------------------------------------------------- unibonさんの書き込み (2007-05-31 19:53) より: this がシステム内で唯一のものなら、たぶんそれで良いのではないでしょうか。 -------------------------------------------------------------------------------- 杏さん、unibonさん、ご回答ありがとうございます。 thisがシステム内で唯一のものであれば、システム内でのSimpleDateFormatへの 同期はうまくいくと思うのですが、「this」はインスタンスそのものなので、 違うクラスのインスタンスがそれぞれのメソッド内で「this」でsynchronizedした場合、SimpleDateFormatクラス内のクラス変数はシステム内で1つだけなので、やはり そのクラス変数に対して複数スレッドの同時アクセスによる不整合が起きてしまう と思われます。 まぁ、ちょっと四の五の言ってる間に検証してみます・・・・・・。 [ メッセージ編集済み 編集者: jim 編集日時 2007-05-31 22:55 ] | ||||||||||||||||
|
投稿日時: 2007-05-31 23:16
メソッド内でnewして使う場合は問題になりませんよ。 フィールドに格納してマルチスレッドで使ったときに問題になります。 なので、以下のような対応は全く無意味です。
インスタンス変数になっていません?当方JDK1.5のソースを見ていますが。
仮にクラス変数を使っている場合、synchronized(this)ではなく、 synchronized(SimpleDateFormat.class)で同期すべきでしょう。 thisで同期を行ったところで、別のインスタンスで同様な処理を行っていたら意味がありません。 スレッドセーフではないものは、意図的にスレッドセーフになるように使わないといけませんが、 メソッド内でライフサイクルを完了するようにすればよいでしょう。 | ||||||||||||||||
|
投稿日時: 2007-05-31 23:28
この場合の「スレッドセーフでない」は、
「Vectorはスレッドセーフだが、Listはスレッドセーフでない」と同じ意味です。 APIドキュメントに書いてあるように、SimpleDateFormatの同一のインスタンスを 複数のスレッドから呼び出してはいけません。 つまり、フォーマット形式が同じだからといって、インスタンスを使いまわしてはいけない、 ということです。 使用する都度にnewしているのであれば、何の心配も要りません。 | ||||||||||||||||
|
投稿日時: 2007-06-01 07:58
自分の解釈間違ってたかな?
と思って不安になったので調べてみました。 http://www.geocities.co.jp/Playtown/1245/java/unsafe_simple_date_format.html このサイトの解説が適切でないようです。
回避方法や再現ソースは合っていそうに思いますが、 何をsynchronizedするかには言及されていないですね。 BugParadeに上がっていたのも、全て同じインスタンスを 複数スレッドで共有した場合に起こったものでした。 複数のインスタンス間で共有しているからではないです。 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4264153 これが「Closed, fixed」になっているのは、
という文章がJDK1.4から追加されたという意味です。 この文に対する直っていないバグがあるのかと思ったのですが、 そういうわけではなさそうです。 Format系のクラスが、 仕様上の外見はImmutableに見えるのに実際はそうではなく、 さらにドキュメントに全く記載がなくて判断できなかった、 という問題が発端になってさらに勘違いされたのでしょう。 | ||||||||||||||||
|
投稿日時: 2007-06-01 08:18
私もnewしてparseなら問題ないという認識でした。
ソースを見直してみたところ、cachedLocaleDataとcachedNumberFormatDataが、 synchronizedなしでアクセスされていますね。 あるキーに対してputが複数回起こる可能性がないわけではないですが、 毎回同じ値だし、Hashtable#get()/put()はsynchronizedだし、問題ないような。 |