2017-02-21 6 views
2
ArrayList<IFoo> objs = new ArrayList<>(); 

public void add(IFoo foo) { 
    objs.add(foo); 
} 

public void loop() { 
    objs.forEach(obj -> { 
     obj.doSomething(message); 
    }); 
} 

этот метод подвержены ошибкам ConcurrentModificationException илиявляется Еогеаспом с лямбдой склонной к Коммодификации ошибки

я должны сделать копию массива

Должен ли я создать итератор?

или должен ли я отмечать оба метода синхронизации?

+1

Вы не должны изменять коллекцию, которую вы итерируете. –

+4

не лучше или хуже, чем обычная итерация «для каждого». – MeBigFatGuy

+0

Это сокращенный способ выполнения 'для объекта (объект obj: objs)', который, в свою очередь, является сокращенным способом получения Итератора и итерации по коллекции. Коллекция, которая обрабатывает коллекцию, отвечает за выброшенные исключения. Итератор ArrayList действительно бросает «ConcurrentModificationException», в то время как итератор получает доступ после его изменения. Поэтому, если ваш код делает это, тогда да. В противном случае нет. – searchengine27

ответ

2

Хорошо, поэтому я помещаю все это в комментарии, но я думаю, что это становится слишком длинным, чтобы положить туда, вот и вот ответ.

Что у вас есть это стенография способ сделать

for(Object obj: objs){ ... } 

, который в своей очереди, является сокращенным способом получения итератора и итерации по коллекции. Iterator, который обрабатывает коллекцию, несет ответственность за выброшенные исключения. Итератор ArrayList действительно бросает исключение ConcurrentModificationException, когда к нему открывается итератор после его изменения. Поэтому, если ваш код делает это, тогда да. В противном случае нет.

ПРИМЕЧАНИЕ: ConcurrentModificationException, хотя содержащий слово Concurrent не обязательно имеет какое-либо отношение к многопоточности. Это очень важное различие. Вы можете получить ConcurrentModificationException быть выброшен просто просто сделать это:

objs.forEach(obj -> {objs.remove(0)}); 

Это правда, что множественная Thread s делает это также может привести к этому, но исключение на самом деле не имеет ничего общего с многопоточности. ConcurrentModificationException просто означает, что ArrayList был изменен одновременно во время его доступа, где одновременный в этом контексте означает в то же время, а не более традиционный вычислительный смысл многопоточности.

EDIT:

Я видел комментарий вы сделали о защите его, а также еще про то, что в Lib вы не можете контролировать. Лучший способ действительно защитить его - это, вероятно, сделать копию List в этом случае. Проблема в том, что lib является черным ящиком, и вы не можете контролировать, изменяют ли они коллекцию и, что более метко, блокируют ли они/блокируют/контролируют свои вставки/удаления из коллекции. Вероятно, создание мелкой копии Списка достаточно.См. Collections#copy(List, List) или ArrayList#ArrayList(Collection)

+0

спасибо, ваше редактирование помогает, вопрос, что, если я помечаю оба метода как «синхронизированный», я получу безопасность из исключения CME. Я фактически разрабатываю эту библиотеку, которую пользователи будут использовать в серверной среде, где она многопоточная. LIB содержит оба метода, и пользователь lib вызывает их – user2727195

+0

Когда я говорил об использовании слова concurrent, я пытался указать, что он не имеет ничего общего с параллелизмом в терминах вычислений. Это просто означает, что он изменяется, поскольку он повторяется. То есть, синхронизация не поможет вам. Если из одного и того же «потока» вы пытаетесь изменить коллекцию во время итерации, синхронизация вам не поможет. Если вы абсолютно уверены, что не можете этого сделать, синхронизация будет защищать от других 'Thread', которые не выполняют итерации (но пока ваш поток) от изменения коллекции. – searchengine27

+0

тот же поток не модифицирует коллекцию, поскольку она итерируется, поэтому каждая операция является атомарной, либо тот же поток добавляет в список, либо выполняет итерацию по списку, но я беспокоюсь о добавлении в список 'threadB', а' threadA' итерирует список. – user2727195

1

Это будет выброшено, если вы измените ArrayList в doSomething или в другом потоке, как и любая форма итерации.

Но если вы этого не делаете, нет причин беспокоиться.

+0

это lib, поэтому я не знаю, как он будет использоваться, возможно, используется в сильно параллельной системе, например, на веб-сервере. – user2727195

+0

массив не изменяется в 'method', однако другой поток может вызвать' add' во время цикла, так что делать в этом случае? – user2727195

+0

В таком случае вы не должны использовать голый ArrayList. Оберните его в Collections.synchronizedList, и в этом случае forEach будет делать правильную вещь автоматически, то есть только один поток будет работать в списке за раз. Нет реализации списка, которая будет лучше, чем это, а не на самом деле. Создание копии списка будет работать, но вам придется заблокировать список, пока вы сделали копию. –

0

Да, если вы изменяете список, например, добавляете или удаляете элементы. Однако, если вы изменяете значение/свойства на итерации объекта, вы должны быть хорошими (как вы пытаетесь).

Ниже приведен пример ConcurrentModificationException с традиционным циклом и использованием Lambda, оба из которых подвержены ошибкам.

import java.util.ArrayList; 
import java.util.List; 

public class Temp { 


    public static void main(String[] args) { 
     List<Integer> l = new ArrayList<>(); 
     l.add(1); 

     for(Integer data :l){ 
      l.add(99); 
     } 

     l.forEach(obj -> { 
      l.add(99); 
     }); 
    } 
} 

В вашем случае, когда вы беспокоитесь о каком-то другом потоке модифицирующего коллекции, пожалуйста, создать копию списка итерации, используйте Collections API, чтобы скопировать массив.

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