1

アイテムのリストがあり、アイテムをチェックすると、リストの最後に移動します。アプリケーションをスレッドセーフにしたい。

リスト「アイテム」に関して「checkItem」のコードはスレッドセーフですか?

  public class ItemListImpl implements ItemList {

        List<Item> items = Collections.synchronizedList(new ArrayList<Item>());

        /**
         * Checks an item and brings it at the end of the list
         */
        public void checkItem(int index) {
            Item item = items.get(index);
            item.check();

            synchronized (items) {
                items.remove(item);
                items.add(items.size(), item);
            }

        }
    }

(アイテムのインデックスを使用するという事実は忘れてください。オブジェクトまたは ID を使用します)

4

2 に答える 2

2

itemsこれは同期リストであり、同期リストはそれ自体をプリミティブミューテックスとして使用することを認識する必要があります。これは、個々のリスト操作のすべてと、synchronizedブロックが同期していることを意味しitemsます。

したがって、同期されていない方法で発生する唯一のポイントは、get呼び出しの完了とsynchronizedブロックの開始の間のギャップです。それで、それは問題ですか?

さて、何が起こるかを考えてみましょう:

  • 他のスレッドが の位置を移動Itemしても問題ありません。とにかく最後に移動するだけです。

  • 他のスレッドが を削除Itemしても問題ありません。への呼び出しはremoveを返しfalseますが、それでも Item を追加し直します。

Item特定の がリスト内の複数の場所に現れる可能性がある場合にのみ、問題が発生しitemsます。その場合、コードがアイテム要素を間違った位置にリストの最後に移動する可能性があります。例えば

  1. スレッド A は get(42) を呼び出して項目 I をフェッチします。
  2. スレッド B は項目 I を位置 1 に挿入します。
  3. スレッド A はチェックを完了し、アイテム I を削除して読み込みます ... しかし、現在は位置 1 にも表示されているため、実際には位置 42 のコピーではなく、そのコピーを削除して追加します。

つまり、指定された Item オブジェクトがリストに最大 1 回出現すると想定できる場合、コードはスレッドセーフです。それを想定できない場合、オブジェクトを間違った位置に移動するリスクがあります。

また、特定のアイテムが複数回出現する可能性がある場合、コードは正しく機能しないことに注意してください。問題は、必ずしも position にあるものではなく、見つかっremove(item)たインスタンスへの最初の参照を削除することです。これを見るには2つの方法があります。この動作が意図しないものである場合、それはバグです。意図したものであれば、スレッドセーフの問題はおそらく問題になりません。itemindex


checkいずれにしても、メソッドがスレッドセーフであると仮定すると、コードはメモリ モデルに関して安全です。

于 2012-08-17T02:34:43.213 に答える
0

スレッドセーフにするには、次の変更を行います。

 public void checkItem(int index) {
            synchronized (items){    
                Item item = items.get(index);
                item.check();          
                items.remove(item);
                items.add(items.size(), item);
            }

    }
于 2012-08-17T02:19:01.827 に答える