2013-04-03 17 views
7

Ponieważ c jest już zsynchronizowanym zbiorem, a zatem jest bezpieczne dla wątków. Ale dlaczego musimy ponownie użyć synchronized(c) dla iteracji? Naprawdę zdezorientowany. Dzięki.Zsynchronizowana kolekcja

„Konieczne jest, aby użytkownik ręcznie zsynchronizować na zwróconej kolekcji podczas iteracji nad nim:

Collection c = Collections.synchronizedCollection(myCollection); 
... 
synchronized(c) { 
    Iterator i = c.iterator(); // Must be in the synchronized block 
    while (i.hasNext()) { 
     foo(i.next()); 
    } 
} 

Niezastosowanie się do tego zalecenia może spowodować niedeterministycznym zachowania.” http://docs.oracle.com/javase/6/docs/api/java/util/Collections.

Odpowiedz

10

Najbardziej dowolna implementacja kolekcji synchronized może zagwarantować synchronizację każdego pojedynczego wywołania metody . Jednak iteracja z konieczności wymaga wielu oddzielnych wywołań metod, więc Ty, użytkownik zbioru synchronicznego, musisz sam przeprowadzić synchronizację całej iteracji.

Na przykład, jeśli nie synchronizować na c zawartość kolekcji może zmienić między i.hasNext() i i.next() - może nawet pójść z konieczności elementy do posiadania nie więcej elementów, w którym to przypadku i.next() byłoby zawodzą.

+0

Co rozumiesz przez "koniecznie obejmuje wiele oddzielnych wywołań metod"? – user697911

+1

@ user697911: 'i.hasNext()' i 'i.next()' są dwoma oddzielnymi wywołaniami metod i nie ma możliwości zastosowania 'Collections.synchronizedCollection' w celu zapewnienia, że ​​nic nie zmieni się pomiędzy' i.hasNext() 'i' i.next() '. –

+0

Tak więc, współbieżnaHashmapa byłaby taka sama? Czy musimy również synchronizować concurrenthashamp w iteracji? – user697911

4

Uczynienie wszystkich metod na indywidualnie zsynchronizowanej klasie nie powoduje agregacji (wywoływania ich w grupie) tych metod. Przez zawijanie Iterator w zsynchronizowanym bloku chronisz to konkretne wystąpienie iteratora przed tym, czy jego pojedyncza metoda jest nazywana przeplatana z innymi wywołaniami przez wiele wątków.

Jeśli zadzwonię .add() raz to jest bezpieczne, jeśli trzeba zadzwonić .add() wiele razy, aby zakończyć logiczne stwierdzenie, nie ma gwarancji, że ktoś inny nie dodała coś innego lub usunięty coś innego między moimi .add() połączeń, chyba że zablokuj wszystko inne, wywołując .add() (lub inną metodę) przez synchronizing dla zmiennej reprezentującej kolekcję.

Iterator sprawia mutiple połączeń do poszczególnych metod w kolekcji, wszystkie one muszą być opakowane w jednym synchronized bloku aby je wykonać jako pojedynczy transaction rodzajów. Zbadaj kod źródłowy implementacji Iterator, zobaczysz co mam na myśli. Oto kod źródłowy dla List wykonuje wiele indywidualnych wywołań do podstawowej implementacji, więc wszystkie muszą być wykonane w nieprzerwanej kolejności przez ten sam wątek, aby były deterministyczne.

@Override 
    public Iterator<A> iterator() { 
     if (tail == null) 
      return emptyIterator(); 
    return new Iterator<A>() { 
     List<A> elems = List.this; 
     public boolean hasNext() { 
     return elems.tail != null; 
     } 
     public A next() { 
       if (elems.tail == null) 
        throw new NoSuchElementException(); 
     A result = elems.head; 
     elems = elems.tail; 
     return result; 
     } 
     public void remove() { 
     throw new UnsupportedOperationException(); 
     } 
    }; 
    } 

source dla AbstractList.iterator() pokazuje jeszcze bardziej skomplikowana logika sprawia, że ​​wiele połączeń.

Lepszym opakowaniem jest zawijanie ich w kolekcjach Immutable, a następnie gwarantuje się, że nic innego nie może zmienić podstawowej kolekcji między połączeniami.

+1

najlepsze wyjaśnienie, z którym się zetknąłem! – kellogs