2013-08-01 16 views
14

Znalazłem to na dos.oracle.comDlaczego muszę synchronizować listy zwrócony przez Collections.synchronizedList

public static List synchronizedList (LIST)

Zwraca zsynchronizowane (thread-safe) lista wspierana przez określoną listę . Aby zagwarantować dostęp szeregowy, ważne jest, aby uzyskać dostęp do listy kopii zapasowych za pośrednictwem zwróconej listy. Konieczne jest, aby użytkownik ręcznie zsynchronizować na zwróconej listy podczas iteracji nad nim:

List list = Collections.synchronizedList(new ArrayList()); 
     ... 
    synchronized(list) { 
     Iterator i = list.iterator(); // Must be in synchronized block 
     while (i.hasNext()) 
      foo(i.next()); 
    } 

Moje pytanie brzmi: Dlaczego muszę zsynchronizować listę, aby go iterację jeśli Collections.synchronizedList(); ma zwracać już zsynchronizowana lista?

Mam tylko dostęp do listy w dwóch wątkach: Jeden wątek po prostu dodaj, a drugi wątek, aby uzyskać i usunąć. Jakie inne klasy polecasz do tego scenariusza?

Dzięki za przeczytanie.

+0

byłby używam 'Collections.synchronizedList()'. Lepiej może zrobić synchronizację samodzielnie w swoim własnym kodzie – JIV

Odpowiedz

17

Lista synchronizowana oznacza tylko, że operacje są zsynchronizowane, a zatem są atomowe. Iteracja nie jest jednak i jeśli wątek adds podczas gdy druga jest iterująca, można uzyskać wyjątek ConcurrentModificationException.

Poprzez ręczną synchronizację bloku powtarzania, upewniasz się, że lista nie jest modyfikowana podczas iteracji.

Jedną z możliwości jest użycie a CopyOnWriteArrayList który zapewnia an iterator że iteracje przez listę jak to było znane podczas iteracji uruchomiony, niezależnie od dalszych modyfikacji. Ta kolekcja nie jest jednak bardzo wydajna, jeśli musisz bardzo często zmieniać zawartość listy.

+0

Dzięki! Chciałbym przegłosować, ale nie mam wystarczającej reputacji. CopyOnWriteArrayList brzmi dobrze, ale tak, to bardzo często proces, CopyOnWriteArrayList może być powolny. – GabrielBB

+0

+1. Skąd wiadomo, że 'add',' remote', itp. Są zsynchronizowane? Nic nie ma w tym javadoc. –

+0

@LuisSep można wywnioskować, że na podstawie faktu, że javadoc mówi, że lista jest zsynchronizowana i iteracja powinna uzyskać monitor listy (co jest zgodne z synchronizowanymi metodami). Spojrzenie na implementację to potwierdza. – assylias

-1

Początkowo byłem nieco zdezorientowany tym tematem, ponieważ większość przykładów znalazłem bez kontekstu. W końcu znalazłem ten wpis na blogu, który wyjaśnił mi wszystko: http://netjs.blogspot.de/2015/09/how-and-why-to-synchronize-arraylist-in-java.html

Z powyższego przykładu wygląda na to, że muszę tylko przekonwertować moją listę za pomocą Collections.synchronizeList(), a następnie mogę dodawać i usuwać elementy bez martwienia się o wątek bezpieczeństwo. Ale tutaj ważne jest, aby pamiętać, że musisz zsynchronizować listę, zanim zostanie przekazana do różnych wątków, ponieważ w przeciwnym razie dostęp do list nie wyklucza się wzajemnie.

więc kompletnym przykładem może być:

public class SynchroProblem implements Runnable{ 
    private List<Integer> myList; 

    //Constructor 
    public SynchroProblem(List<Integer> myList){ 
    this.myList = myList; 
    } 

    @Override 
    public void run() { 
    // Do stuff with the list .add(), .remove(), ... 
    myList.add(5); 

    // Even if mylist is synchronized the iterator is not, 
    // so for using the iterator we need the synchronized block 
    synchronized (myList){ 
     // do stuff with iterator e.g. 
     Iterator<Integer> iterator = myList.iterator(); 
     while (iterator.hasNext()){ 
     int number = iterator.next(); 
     if (number == 123){ 
      iterator.remove(); 
     } 
     } 

    } 
    } 

    public static void main(String[] args) { 

    List<Integer> originalList = new ArrayList<Integer>(); 

    // Synchronize list 
    List<Integer> syncList = Collections.synchronizedList(originalList); 

    // Create threads and pass the synchronized list 
    Thread t1 = new Thread(new SynchroProblem(syncList)); 
    Thread t2 = new Thread(new SynchroProblem(syncList)); 

    t1.start(); 
    t2.start(); 

    } 
}