5

私は次のようなクラスを持っています:

class Test
{
    private LinkedList<Person> persons = new LinkedList<Person>;

    public synchronized void remove(Person person)
    {
        persons.remove(person);
    }

    public List<Person> getAllPersons()
    {
        // Clients may iterate over the copy returned and modify the structure.
        return new ArrayList<Person>(persons);
    }
}

persons同時に変更できます。1つはremove()1つのスレッドによるもので、2つはによって返される浅いコピーされたインスタンスによるものgetAllPersons()です。

マルチスレッド環境で上記のシナリオをテストして、が呼び出されたConcurrentModificationExceptionときに浅いコピーを返すことで回避できるかどうかを確認しました。getAllPersons()うまくいったようです。私は一度もに遭遇したことがありませんConcurrentModificationException

この場合、なぜ浅いコピーだけを作成するのはpersons避けConcurrentModificationExceptionますか?

4

2 に答える 2

6

開いているイテレータを無効にする方法でコレクションが変更されると、ConcurrentModificationExceptionがスローされます。これは通常、スレッドセーフではないコレクションが複数のスレッドによってアクセスされた場合に発生します(これが唯一の原因ではありませんが)

コードにはまだ小さなエラーがあります。それ自体がスレッドセーフではないメンバーに安全にアクセスするにはsynchronize、getAllPersonsメソッドを使用する必要があります。

それが修正されていると仮定すると、コピーを返すため、コレクション自体を他の呼び出し元が変更することはできません(それぞれが独自のコピーを取得します)。つまり、ConcurrentModificationExceptionを取得することはできません。

これPerson、クラスのスレッドセーフの問題から保護するのではなく、コレクション自体のみを保護することに注意してください。が不変の場合Personは、大丈夫です。

この場合、より良い解決策は、同様のセマンティクスを実装するCopyOnWriteArrayListを直接使用することですが、リストから読み取るたびにではなく、実際にリストに書き込むときにのみコピーします。

于 2011-08-24T02:54:06.813 に答える
1

これは、リスト自体ではなく、リストのコピーを返すためです。remove()実際のリストを変更する唯一の方法であり、複数のスレッドからアクセスできます。メソッドを呼び出すスレッドgetAllPersons()はとにかく新しいリストを取得するため、このリストを変更しても、元のリストは変更されません。したがって、コレクションがスレッドによって同時に変更されていないため、ConcurrentModificationExceptionが発生していません。

于 2011-08-24T05:01:16.737 に答える