1

同期されたコード ブロックに非同期コレクションが含まれている場合。コレクションはスレッドセーフと見なされますか? そうでない場合、2 つのスレッドが同期されたコード内のコレクションに安全にアクセスできないという実用的なシナリオを提供できますか?

ありがとう。

4

3 に答える 3

4

これは、別の例で使用した非常に単純なオブジェクト、無制限のキューです。

public final class MyQueue<T> {

    private List<T> list = new ArrayList<T>();

    public T take() {
        synchronized(list) {
            while (list.size() == 0) {
                list.wait();
            }
            return list.remove(0);
        }
    }

    public void put(T object) {
        synchronized(list) {
            list.add(object);
            list.notifyAll();
        }
    }
}

ここにはカプセル化された ArrayList があり、同期されたメソッドを介してのみアクセスまたは変更できます (すべての同期されたメソッドは、ArrayList である同じロックを使用します)。したがって、スレッドセーフです。ArrayList のメソッド自体が同期されていなくてもかまいません。

于 2013-01-28T21:10:05.980 に答える
1

コレクションがその1つの同期ブロック内でのみアクセスされること、またはコレクションへのすべてのアクセスが同じオブジェクトの同期ブロックに囲まれていることを保証できる場合、安全であるはずですが、それを証明するのは非常に難しい仮定です. 、あなたの後に来るかもしれない他の開発者によって簡単に壊れる可能性があります。

于 2013-01-28T21:07:16.137 に答える
0

コレクションにアクセスするすべてのコードが同期され、それらが同じ「オブジェクト」を使用して同期する場合のみ。

たとえば、以下のコードは異なるオブジェクトに同期されているため、同期されません。

public class Foo {
    private final Collection<object> collection;

    public void Foo(Collection<object> collection) {
       this.collection = collection;
    }

    public synchronized add(Object o) {
       this.collection.add(o);
    }
}

public class Bar {
    private final Collection<object> collection;

    public void Bar(Collection<object> collection) {
       this.collection = collection;
    }

    public synchronized print() {
       for (Object o : collection) { System.out.println(o); }
    }
}

Object次に、 o が以前に追加されたと思ったために o が出力されることを期待していたのに、実行中のスレッドが追加が完了する前に停止したという状況が発生する可能性があります。

ある場所にアクセスできることを示すフラグを持っている人がいる場合のように、これを想像するのは簡単です. 旗が高く上がっていると、ブロックに入ることができません。この人物は、Class インスタンスを作成するときに常に作成され、それにバインドされます。したがって、次のコードでは、3 つの「フラグ担当者」が表示されます。

...
Collection<Object> objs = new ArrayList<Object>();
Foo foo = new Foo(objs);
Bar bar = new Bar(objs);
...

このsynchronizedステートメントは、誰かが旗を通過した後に旗を上げ、ブロックに存在するときに旗を下ろすように旗の人を示しています。クラスメソッドに同期を設定したため、そのインスタンスの「フラグの人」にバインドされます。したがって、さまざまな「フラグの人」が、コレクションが処理されるブロックに入る誰かに手を挙げますが、両者は互いに同期されていないため、他の人がフラグを立てていても、誰でも入ることができます。

これを解決するには、フラグを処理する人が 1 人だけで済みます。これを行うには、共有フラグ担当者が必要です。この場合、コレクション自体を使用できます。だから、あなたは次のようなものを持つでしょう

public class Foo {
    private final Collection<object> collection;

    public void Foo(Collection<object> collection) {
       this.collection = collection;
    }

    public synchronized add(Object o) {
       synchronized (collection) {
           this.collection.add(o);
       }
    }
}

public class Bar {
    private final Collection<object> collection;

    public void Bar(Collection<object> collection) {
       this.collection = collection;
    }

    public print() {
       synchronized (collection) {
           for (Object o : collection) { System.out.println(o); }
       }
    }
}

コレクションの「フラグの人」だけがフラグを立てているため、誰もが「誰が最初に終了したか」ではなく「誰が最初に来たか」に従ってコレクションにアクセスします。

説明を少し難しくしたと思いますが、参考になれば幸いです。私がここに描くことができれば、おそらくよりよく理解されるでしょう:P

于 2013-01-28T21:21:19.903 に答える