2013-08-01 2 views
14

я нашел это в dos.oracle.comПочему мне нужно синхронизировать список возвращенного Collections.synchronizedList

общественного статический список synchronizedList (список List)

Возвращает синхронизированный (поточно-), который поддерживается указанным списком . Чтобы гарантировать последовательный доступ, крайне важно, чтобы весь доступ к списку поддержки выполнялся через возвращаемый список. Крайне важно, чтобы пользователь вручную синхронизировать возвращенного списка при переборе над ним:

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

Мой вопрос: Почему я должен синхронизировать список итерацию, если Collections.synchronizedList(); должен возвращать уже синхронизированный список?

Я просто присоединяюсь к списку в двух потоках: один поток просто добавляет, а другой поток - для получения и удаления. Какие другие классы вы рекомендуете использовать для этого сценария?

Спасибо за чтение.

+0

я Wouldnt использовать 'Collections.synchronizedList()'. Может быть, лучше сделать синхронизацию самостоятельно в собственном коде – JIV

ответ

17

Синхронизированный список означает, что add, remove и т. Д. Операции синхронизированы и поэтому являются атомарными. Итерация, однако, отсутствует, и если поток adds, а другой - итерация, вы можете получить исключение ConcurrentModificationException.

Ручная синхронизация вашего итерационного блока гарантирует, что список не будет изменен во время итерации.

Одна альтернатива состоит в использовании a CopyOnWriteArrayList, который обеспечивает an iterator, перебирающий списке как это было известно, когда итерации начала, независимо от последующих модификаций. Однако эта коллекция не очень эффективна, если вам нужно очень часто менять содержимое списка.

+0

Спасибо! Я бы поднял его, но у меня недостаточно репутации. CopyOnWriteArrayList звучит неплохо, но да, это очень часто процесс, CopyOnWriteArrayList может быть медленным. – GabrielBB

+0

+1. Как вы знаете, что 'add',' remote' и т. Д. Синхронизированы? В javadoc ничего нет. –

+0

@ LuisSep вы можете заключить, что на основании того факта, что javadoc говорит, что список синхронизирован, и итерация должна получить монитор списка (который согласуется с синхронизируемыми методами). Взгляд на реализацию подтверждает это. – assylias

-1

Сначала я был немного смущен этой темой, потому что большинство примеров, которые я нашел, были без контекста. Я, наконец, нашел этот пост в блоге, который очистил меня для меня: http://netjs.blogspot.de/2015/09/how-and-why-to-synchronize-arraylist-in-java.html

Из приведенного выше примера мне просто нужно преобразовать свой список с Collections.synchronizeList(), а затем я могу добавлять и удалять элементы, не беспокоясь о потоке безопасность. Но здесь важно отметить, что вы должны синхронизировать список, прежде чем он будет передан различным потокам, потому что в противном случае доступ к спискам не будет взаимоисключающим.

Так полный пример будет:

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

    } 
} 
Смежные вопросы