- PR -

SimpleDateFormat使用の際の同期化について

投稿者投稿内容
jim
会議室デビュー日: 2002/09/13
投稿数: 5
投稿日時: 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/02
投稿数: 189
お住まい・勤務地: 最北の地
投稿日時: 2007-05-31 19:13
メソッド内であれば スレッドセーフ、クラス単位のであれば 場合により崩れる
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-05-31 19:53
引用:

jimさんの書き込み (2007-05-31 18:59) より:
しかしながら、どうも解せないのでSimpleDateFormatのソースを確認したところ、
同クラスはクラス変数として、フォーマット形式("yyyyMMdd"等)を保持
しているように見受けられるので、上記の同期処理ではSimpleDateFormatクラスが
クラス変数として持っているメンバへ各クラスからスレッドが同時にアクセスしてしまう
ように思われます。


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 はスレッドセーフです」というのとは意味が違い、より致命的なんですよね。この問題を指し示す、なにかもっと的確な用語ってあるのでしょうか?「スレッドセーフ」だけだと問題が区別しにくいですよね。

引用:

jimさんの書き込み (2007-05-31 18:59) より:
これを回避するには、特定のクラスに唯一のロックオブジェクトを定義し、
各クラスからは、このロックオブジェクトでsynchronizedしなければならないと
思われるのですが、上記のコードで正常な同期は図れるのでしょうか?


this がシステム内で唯一のものなら、たぶんそれで良いのではないでしょうか。

--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
jim
会議室デビュー日: 2002/09/13
投稿数: 5
投稿日時: 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 ]
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2007-05-31 23:16
引用:

掲題の件について、どなたかご存知の方がいたら教えてください。
現在、とあるWebシステムを作っているのですが、
その過程で「SimpleDateFormat(その他複数のFormat系クラス)はスレッドセーフでないので、
同期処理をしなければならない」という情報を得ました。


メソッド内でnewして使う場合は問題になりませんよ。
フィールドに格納してマルチスレッドで使ったときに問題になります。
なので、以下のような対応は全く無意味です。

引用:

私が作っているシステムでも複数のソースで同クラスを使用しており、
同じプロジェクトのメンバー曰く、下記のようにするべきと指示がありました。

synchronized( this ) {
   String strDate = "20050101";
   SimpleDateFormat formatter = new SimpleDateFormat( "yyyyMMdd" );
   Date date = formatter.parse( strDate );
}



引用:

しかしながら、どうも解せないのでSimpleDateFormatのソースを確認したところ、
同クラスはクラス変数として、フォーマット形式("yyyyMMdd"等)を保持
しているように見受けられるので、上記の同期処理ではSimpleDateFormatクラスが
クラス変数として持っているメンバへ各クラスからスレッドが同時にアクセスしてしまう
ように思われます。


インスタンス変数になっていません?当方JDK1.5のソースを見ていますが。

引用:

これを回避するには、特定のクラスに唯一のロックオブジェクトを定義し、
各クラスからは、このロックオブジェクトでsynchronizedしなければならないと
思われるのですが、上記のコードで正常な同期は図れるのでしょうか?


仮にクラス変数を使っている場合、synchronized(this)ではなく、
synchronized(SimpleDateFormat.class)で同期すべきでしょう。
thisで同期を行ったところで、別のインスタンスで同様な処理を行っていたら意味がありません。

スレッドセーフではないものは、意図的にスレッドセーフになるように使わないといけませんが、
メソッド内でライフサイクルを完了するようにすればよいでしょう。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2007-05-31 23:28
この場合の「スレッドセーフでない」は、
「Vectorはスレッドセーフだが、Listはスレッドセーフでない」と同じ意味です。

APIドキュメントに書いてあるように、SimpleDateFormatの同一のインスタンスを
複数のスレッドから呼び出してはいけません。
つまり、フォーマット形式が同じだからといって、インスタンスを使いまわしてはいけない、
ということです。
使用する都度にnewしているのであれば、何の心配も要りません。

あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2007-06-01 07:58
自分の解釈間違ってたかな?
と思って不安になったので調べてみました。

http://www.geocities.co.jp/Playtown/1245/java/unsafe_simple_date_format.html
このサイトの解説が適切でないようです。

引用:

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


回避方法や再現ソースは合っていそうに思いますが、
何をsynchronizedするかには言及されていないですね。

BugParadeに上がっていたのも、全て同じインスタンスを
複数スレッドで共有した場合に起こったものでした。

複数のインスタンス間で共有しているからではないです。

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4264153

これが「Closed, fixed」になっているのは、
引用:

同期
decimalフォーマットは同期化されません。スレッドごとに別のフォーマットインスタンスを作成することをお勧めします。複数のスレッドがフォーマットに同時にアクセスする場合は、外部的に同期化する必要があります。


という文章がJDK1.4から追加されたという意味です。

この文に対する直っていないバグがあるのかと思ったのですが、
そういうわけではなさそうです。

Format系のクラスが、
仕様上の外見はImmutableに見えるのに実際はそうではなく、
さらにドキュメントに全く記載がなくて判断できなかった、
という問題が発端になってさらに勘違いされたのでしょう。
mio
ぬし
会議室デビュー日: 2005/08/25
投稿数: 734
お住まい・勤務地: 神奈川県
投稿日時: 2007-06-01 08:18
私もnewしてparseなら問題ないという認識でした。
ソースを見直してみたところ、cachedLocaleDataとcachedNumberFormatDataが、
synchronizedなしでアクセスされていますね。
あるキーに対してputが複数回起こる可能性がないわけではないですが、
毎回同じ値だし、Hashtable#get()/put()はsynchronizedだし、問題ないような。

スキルアップ/キャリアアップ(JOB@IT)