- PR -

parse時にNumberFormatExceptionが発生する

投稿者投稿内容
ひろ@ya
大ベテラン
会議室デビュー日: 2006/02/23
投稿数: 168
投稿日時: 2008-07-09 10:19
DateFormatとそのサブクラス(SimpleDateFormat等)スレッドセーフではありません。

JDK1.4の場合、parseの途中経過とformatの処理対象日付を、同一のインスタンス変数に保持するという、スレッドセーフを一切考慮していない実装が行われています。

これがどれほど危険なことかわかりますよね。
arare
会議室デビュー日: 2003/11/05
投稿数: 13
投稿日時: 2008-07-09 10:30
>nagiseさん
ご指摘ありがとうございます。
質問させてください。

引用:

nagiseさんの書き込み (2008-07-08 18:55) より:
そもそもSimpleDateFormatをstaticフィールドに保持しないようにしましょう。
毎回生成して使い捨てるのが単純な対処法です。



これは
public boolean valid(String date) {}
で宣言する と認識しております。

一方、教えていただいた
「SimpleDateFormatでNumberFormatException 」のページよりリンクしている
「java.text.SimpleDateFormat はスレッドセーフではない 」を参照すると
synchronized宣言で囲むことを対処方法としており
public static synchronized boolean valid(String date) {}
で宣言する と認識しております。

また、デメリットとしては
前者は、メモリの消費
後者は、待ち時間の発生
が考えられると認識しております。

この場合、どちらがより良い対処方法になるのでしょうか?
また、認識違いがありましたらご指摘下さい。

arare
会議室デビュー日: 2003/11/05
投稿数: 13
投稿日時: 2008-07-09 10:59
引用:

ひろ@yaさんの書き込み (2008-07-09 10:19) より:

JDK1.4の場合、parseの途中経過とformatの処理対象日付を、同一のインスタンス変数に保持するという、スレッドセーフを一切考慮していない実装が行われています。

これがどれほど危険なことかわかりますよね。



わかります。
危険ですね。
不可解な現象が多いわけです。
(わかってしまえば不可解ではありませんが・・・)
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2008-07-09 13:26
引用:

また、デメリットとしては
前者は、メモリの消費
後者は、待ち時間の発生
が考えられると認識しております。


都度生成しても、メモリはGCによって解放されますので、
あまり気にするポイントではありません。
インスタンスの生成コストくらいでしょうか。

WEBのように複数スレッドによる参照が発生する場合、
同期コストの方が問題となります。

同期コストをかけずに、かつインスタンス生成を減らす方法としては、
スレッドローカルを使うという方法も有ります。
コード:
//保持側
private static ThreadLocal formatHolder = new ThreadLocal(){
    protected Object initValue(){
        return new SimpleDateFormat("....");
    }
};

//利用側
SimpleDateFormat format = (SimpleDateFormat)formatHolder.get();


SimpleDateFormatのインスタンスはスレッド毎に生成されますが、
同一スレッドにおいてすでに生成済みであれば、それ以降は生成されません。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2008-07-09 14:30
引用:

arareさんの書き込み (2008-07-09 10:30) より:
「SimpleDateFormatでNumberFormatException 」のページよりリンクしている
「java.text.SimpleDateFormat はスレッドセーフではない 」を参照すると
synchronized宣言で囲むことを対処方法としており
public static synchronized boolean valid(String date) {}
で宣言する と認識しております。

また、デメリットとしては
前者は、メモリの消費
後者は、待ち時間の発生
が考えられると認識しております。

この場合、どちらがより良い対処方法になるのでしょうか?



「SimpleDateFormatでNumberFormatException 」のコメント欄での議論がありますが、かつのりさん調べによれば少なくともインスタンスが別であれば問題がないという結論ですね。
「java.text.SimpleDateFormat はスレッドセーフではない」で書かれているように、唯一のスレッドからしか使わないとまでする必要はないようです。

ですから、

  • SimpleDateFormatのインスタンスを使い捨てにする
  • ThreadLocalを用いてThreadごとに単一のインスタンスしか使わないようにする

あたりの対処法が無難でしょうか。
synchronizedによる同期はロックが発生するのであまり好ましくないように思います。パースのたびにロックするとしたら、回数も尋常ではないでしょうし。

世代の短いオブジェクトのGCは高速なのでメモリについてはそれほど心配することはありません。
SimpleDateFormatだとオブジェクトの生成コストがやや高いような気もしますが、全体のパフォーマンスにクリティカルに影響するようなものではないでしょう。このあたりは実測してみないとなんとも言えませんが。

それでも気になるならThreadLocalでオブジェクトを使いまわすようにしておけばいいのではないでしょうか。サンプルはかつのりさんが提示していますね。
arare
会議室デビュー日: 2003/11/05
投稿数: 13
投稿日時: 2008-07-10 13:20
かつのりさん
nagiseさん

回答ありがとうございます。
ご意見、とても参考になります。
まずSimpleDateFormatのインスタンスを使い捨てにする対処を行い
実測の結果問題があるようなら
ThreadLocalを使用する方法に転換したいと考えています。

ありがとうございました。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2008-07-10 17:14
引用:

nagiseさんの書き込み (2008-07-07 16:38) より:
手前味噌ですが。
SimpleDateFormatでNumberFormatException

スレッドセーフではないSimpleDateFormatをマルチスレッド下で使った場合の現象です。


これはそのページの、みなさんのコメントまで含めて読む必要があるわけですね。
最初、コメントをすっ飛ばして本体だけ読んでいて、ええっ、と思ってしまいました。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2008-07-10 18:15
引用:

unibonさんの書き込み (2008-07-10 17:14) より:
最初、コメントをすっ飛ばして本体だけ読んでいて、ええっ、と思ってしまいました。



うーむ。こういう時にさっと提示できるようにちゃんとまとめたエントリにしておいた方がいいですね…。下手に読ませると逆に誤解を招くかもしれないですねぇ。

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