Javaの同期化の質問です。HashMapは同期化されないことが明言されていたので、Hashtableを使って「これで安全」と思っていたところ、Enumeratorを使ってループをしているコードで

ConcurrentModificationExceptionが送出されました。for文の前後で明示的にロック・アンロックすべきなのでしょうか?またなにか慣用句的なコードはありますか?

回答の条件
  • 1人2回まで
  • 登録:2006/07/04 16:26:37
  • 終了:2006/07/11 16:30:03

回答(4件)

id:quintia No.1

quintia回答回数562ベストアンサー獲得回数712006/07/04 17:36:39

ポイント23pt

Hashtable のすべての「コレクションビューメソッド」によって返される Collection の iterator メソッドおよび listIterator メソッドによって返される Iterator は、「フェイルファスト」です。Iterator の作成後に、Iterator 自体の remove メソッドまたは add メソッド以外の方法で Hashtable が構造的に変更されると、Iterator は ConcurrentModificationException をスローします。

http://java.sun.com/j2se/1.3/ja/docs/ja/api/java/util/Hashtable....

iterator メソッドや listIterator メソッド で返されるイテレータを通しての操作に関しては、同期は保証されず「フェイルファスト」つまり「データが操作された可能性があるならば即座に例外を発生して処理を中断する」仕様です。


http://java.sun.com/j2se/1.3/ja/docs/ja/api/java/util/Collection...

java.util.Collections の static メソッド synchronizedMap を使うのが常套です。

ただし、Hashtable での iterator メソッドと同じ問題が存在しています。

返された Map への直接のアクセスは同期されますが、entrySet() keySet() values() メソッドを使う場合は、補足説明されている通りの同期処理を書く必要があります。

synchronizedMap メソッドの説明とサンプルコードをご覧ください。

id:westfish

なるほど、「Hashtable自体はスレッドセーフだが、Hashtable.values()などの返り値は必ずしもスレッドセーフではない」ということですね。

2006/07/05 15:02:18
id:bellbind No.2

bellbind回答回数6ベストアンサー獲得回数12006/07/05 11:04:54

ポイント23pt

使用状況がわからないので明確な答えは出せませんが、Java5であればjava.util.ConcurrentHashMapである程度の典型的状況は解決可能だと思います。

id:westfish

使用状況をおおざっぱに説明しますと、あるHashtable Xを毎秒数十回読み出して画面にグラフィック描画をするスレッドと、XML-RPCでクエリーを受けてXに要素を追加するスレッドがあるような状況です。リンク先を読んでみます。

2006/07/05 15:11:19
id:quintia No.3

quintia回答回数562ベストアンサー獲得回数712006/07/05 16:31:16

ポイント22pt

1.の回答者です。

コメントの

「Hashtable自体はスレッドセーフだが、Hashtable.values()などの返り値は必ずしもスレッドセーフではない」ということですね。

「スレッドセーフではない」という表現には違和感があります。


あるスレッドが clear メソッドを実行中に、別のスレッドが remove メソッドを実行したとします。


「スレッドセーフでない」というのは、例えば、remove した要素を clear がもう一度取り除こうとしておかしくなる、という様な状況を意味するでしょう。


Hashtable が持つ add, clear, contains, remove, size, (etc...) のメソッド群はスレッドセーフです。

「スレッドセーフである」というのは、clear メソッドと remove メソッドの両方が同一時点で実行されている、という状況が起らないということです。


そして、Hashtable#values() で取り出したコレクションビュー(Collection のインスタンス) が持つ remove, size などのメソッドも、取り出し元の Hashtable のメソッド群に対してスレッドセーフです

Hashtable の clear メソッドと、Hashtable#values で取り出したコレクションビューの removeメソッドは同期されます。Hashtable#clear メソッドと Collections#remove メソッドが同一時点で実行されている、という様なことはありません。(JDK1.4 の Hashtable.java を読むと、Hashtable のインスタンス自身で同期されています)


Hashtable#values でコレクションビューを取り出した後で、Hashtable の add, clear, remove などの要素を操作してしまうメソッドを実行すると、取り出し済のコレクションビューも影響を受けます。

コレクションビューの側が、すでに変化してしまった状態を取り出したりしないように、例外を発生させて状態が変化したことを知らせる様になっているのです。それが「フェイル・ファスト」の概念です。

逆に言うと、contains や size などの要素を操作しないメソッドは問題ない――つまり同期されているし、また、コレクションビューの側の remove なども同期されます。(ただし2つのコレクションビューを取り出している時には、片側で remove するともう一方で ConcurrentModificationException が発生するわけですが)


「フェイル・ファスト」なコレクションビューではなく、「一貫性のある」コレクションビューを提供するのが、2.の回答の ConcurrentHashMap ですね。ただその代償として、

これらが ConcurrentModificationException をスローすることはありませんが、一度に 1 つのスレッドのみが反復子を使用するように設計されています。

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurre...

ということになるわけです。

id:westfish

なるほど、スレッドセーフという言葉の意味を勘違いしていたようです。スレッドセーフにするための方法として、イテレータの使用中は追加や削除ができないようにsynchronizedブロックで囲む方法と、オブジェクトがおかしくなる前に例外を投げて止めてしまう方法とがあるわけですね。

結局のところ、コレクションフレームワークを使った解決方法では、コレクションのインスタンスがイテレーションの終了を知ることができない為に、「イテレーションしている間は追加削除をさせない」ということが困難なわけですね。自分でイテレーションの部分と追加部分をsyncronizedで囲うのが一番適切な対処法であるように思えてきました。

2006/07/10 16:41:17
id:bellbind No.4

bellbind回答回数6ベストアンサー獲得回数12006/07/06 08:39:36

ポイント22pt
あるHashtable Xを毎秒数十回読み出して画面にグラフィック描画をするスレッドと、XML-RPCでクエリーを受けてXに要素を追加するスレッドがあるような状況です。

まず、書き込みのほうですが、追加するスレッドが何個もあるようであれば、ConcurrentHashMapを使うとHashtableより効率はよくなります。それは先に紹介したdeveloperWorksのほうのリンクにも言及があります。


また描画スレッドなどでコレクションデータ全体を走査する場合はそのコレクションを直接走査するのではなく、参照するスレッド(ループ)の開始時などで、一度ConcurrentHashMapをHashMapにコピー(たとえば、Map viewMap = new HashMap(storeMap))して、それを使うというのがセオリーでしょうか。


描画や計算などでは、ある時点でのデータコレクションのスナップショットを取ってそれを処理に使うわけです。これは普通のHashMapをロックして使う場合や、Hashtableを使う場合、それ以外のデータ構造であっても同様です。

id:westfish

グラフィック描画は秒間数十回行われています。そのたびにコレクションをコピーするのはあまり現実的なソリューションではないと思います。

やはり描画や計算の開始時点でロックをかけるのがよさそうです。

2006/07/10 16:47:54

コメントはまだありません

この質問への反応(ブックマークコメント)

トラックバック

  • ラシウラ 2006-07-11 22:13:10
  • Mapの同期化・・・ ちょっとそんな話題が出て、勘違いしている人が多いというか、多そうなので。 詳しくは以下を参考に。 http://q.hatena.ne.jp/1151997995 http://www.ibm.com/developerworks/jp/java/library/j-j
  • 【java】 HashtableとHashMapの違い ■Hashtable ・同期を取る ■HashMap ・同期を取らない この辺を参考に見てみる。 http://q.hatena.ne.jp/1151997995 調査のきっかけは既存のソースの中にHashtableが出てきた
「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません