「//fix this/」の記事インデックス

//fix this/ Javaコードの間違いをさがせ!


このシリーズは、あなたのコーディング・スキルを試すコーナーです。毎号オラクルのJavaエバンジェリストたちが、Javaコードに関する難問を出題し、正解と解説を掲載します。是非、毎号チャレンジして貴方のコーディング能力の向上にお役立て下さい。


第13回目 (Java Magazine 2014/Mar-Aprからの出題)


1. 今回のテーマ 「並行処理」 

カウンタのコレクションが必要であるとします。各カウンタは名前で識別され、42 からカウントが始まります。そのため、カウンタ名を引数として受け取り、そのカウンタをインクリメントして新しい値を返すか、またはそのカウンタ名を初めて使用する場合は 42 に初期化するような increment メソッドを実装する必要があります。



2. コード

次のようなコードを思い付きました。


private final ConcurrentMap

しかし、このコードでは、なぜかインクリメントの試行が「途中で止まる」(つまり、increment メソッドが 2 回呼び出されたのにカウンタが 1 回しかインクリメントされない)ことがあります。



3.正しいのはどれ?

上記のコードの正しい修正方法を選んでください。

1) メソッド宣言に synchronized キーワードを追加する。

2) ConcurrentHashMap の代わりに Hashtable を使用する。

3) 楽観的並行性スタイルのアルゴリズムを実装する。

4) ConcurrentHashMap の代わりに AtomicLongArray を使用する。

<ヒント>
ときには、ひたすら頑張ることも必要です。(^^;






【答え】

正解は3)です。提示されたコードの問題点は、個々のcnts呼出しの間に競合状態が存在することです。たとえば、スレッド1がカウンタXの値を読み取った後に、スレッド2が同じ値を読み取る場合を考えてみましょう。両方のスレッドがインクリメント値を計算して返すため、インクリメント結果は予想されるa+2ではなく、a+1になります。カウンタの初期化に関しても、同様の競合状態が発生します。

1)でも問題は修正されますが、ConcurrentHashMapを使用する効果がなくなります。2番は同じ問題(完結した一連のイベントとしてではなく個々の処理が原子的に実行される)が発生するため、修正方法にはなりません。4)でも問題を修正できますが、余分な処理が必要になります。


<< 第12回へ戻る I 第14回へ進む >>

「//fix this/」の記事インデックス