2013-04-03 12 views
7

Poiché c è già raccolta sincronizzata, ed è quindi thread-safe. Ma perché dobbiamo usare ancora synchronized(c) per l'iterazione? Davvero confuso. Grazie.Raccolta sincronizzata

"E 'indispensabile che l'utente sincronizzare manualmente sulla raccolta restituita quando l'iterazione su di esso:

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

La mancata osservanza di questi consigli può causare un comportamento non deterministico." http://docs.oracle.com/javase/6/docs/api/java/util/Collections.

risposta

10

La maggior parte delle implementazioni di raccolta di synchronized è possibile per garantire che ogni singolo metodo chiamata sia sincronizzato. Ma l'iterazione coinvolge necessariamente più chiamate di metodo separate, quindi tu, utente della collezione sincronizzata, devi sincronizzarti da solo sull'intera iterazione.

Per esempio, se non si sincronizzano su c, il contenuto della raccolta potrebbe cambiare tra i.hasNext() e i.next() - potrebbe anche andare da avere elementi a non avere più elementi, nel qual caso i.next() fallirebbe.

+0

Cosa intendi per "implica necessariamente più chiamate di metodo separate"? – user697911

+1

@ user697911: 'i.hasNext()' e 'i.next()' sono due chiamate di metodo separate e non esiste alcun modo per l'implementazione di 'Collections.synchronizedCollection' per garantire che nulla cambi tra' i.hasNext() 'e' i.next() '. –

+0

Quindi, concurrentHashmap sarebbe lo stesso? Dobbiamo anche sincronizzare il concurrenthashamp in iterazione? – user697911

4

Rendere tutti i metodi su una classe individualmente sincronizzati non rende aggregabili (chiamandoli in un gruppo) di questi metodi thread safe. Racchiudendo lo Iterator in un blocco sincronizzato, si protegge quella particolare istanza dell'iteratore dal fatto che il suo singolo metodo è denominato intercalato con altre chiamate da più thread.

Se chiamo .add() una volta che è sicuro, se ho bisogno di chiamare .add() più volte per completare una frase di senso, non v'è alcuna garanzia che qualcun altro non ha aggiunto qualcos'altro o qualcosa rimosso altro tra i miei .add() chiamate a meno che non blocca tutto il resto chiamando lo .add() (o qualsiasi altro metodo) per synchronizing sulla variabile che rappresenta la raccolta.

Il Iterator rende mutiple chiamate ai singoli metodi per la raccolta, tutti devono essere avvolti in un unico blocco synchronized per farli eseguire come un singolo transaction di sorta. Esaminare il codice sorgente dell'implementazione di Iterator vedrete cosa intendo. Ecco il codice sorgente per List effettua più chiamate individuali all'implementazione sottostante, quindi devono essere eseguite in ordine ininterrotto dallo stesso thread per essere deterministiche.

@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(); 
     } 
    }; 
    } 

Il source per AbstractList.iterator() mostra ancora logica più complicato che rende più chiamate.

Un wrapper migliore li sta avvolgendo nelle raccolte Immutable, quindi si garantisce che nient'altro può modificare la raccolta sottostante tra le chiamate.

+1

migliore spiegazione che ho trovato! – kellogs

Problemi correlati