2010-04-21 5 views
8

Я рассматриваю этот кусок кода. Этот конструктор делегирует нативный метод «System.arraycopy»Java - безопасность потоков конструкторов ArrayList

Реальная безопасность? И я имею в виду, может ли это когда-нибудь бросить ConcurrentModificationException?

public Collection<Object> getConnections(Collection<Object> someCollection) { 
    return new ArrayList<Object>(someCollection); 
} 

ли какая-нибудь разница, если коллекция копируется в THREADSAFE например CopyOnWriteArrayList?

public Collection<Object> getConnections(CopyOnWriteArrayList<Object> someCollection) { 
    return new ArrayList<Object>(someCollection); 
} 

Edit: Я знаю, что THREADSAFE = ConcurrentModificationException!. Я пытаюсь сделать снимок данных в определенный момент времени. Поэтому, если другой Thread пишет someCollection на полпути через копию, мне не важно, имеет ли результат новый объект или нет. Я просто не хочу, чтобы бросить ConcurrentModificationException или хуже

ответ

4

Ваш вопрос заключается в том, можете ли вы безопасно получить снимок коллекции, которая может подвергаться одновременной модификации другим потоком, используя new ArrayList<Foo>(thatCollection). Ответ: до тех пор, как thatCollection сам по себе является потокобезопасным, да. Так что если это CopyOnWriteArrayList, synchronizedList или Vector, если это не потокобезопасно, например, если это еще один ArrayList, вы не в порядке. (Что будет может быть хуже, чем ConcurrentModificationException.)

Причина заключается в том, что ArrayList конструктор делает только один атомный вызов другой коллекции - его метод toArray. Таким образом, он по существу пользуется любыми гарантиями безопасности потоков, которые сам метод имеет. Это не всегда было реализовано так, но теперь именно по этой причине. Мы делаем то же самое в Гуаве с ImmutableList.copyOf.

7

делегатов Этот конструктор для нативного метода «System.arraycopy»

На самом деле, это вызывает toArray() на someCollection. Это в конечном итоге вызовет System.arraycopy, если someCollection является ArrayList. Для других типов коллекций массив будет создан другими способами.

Резьба безопасна?

No.

И что я имею в виду это может когда-нибудь бросить ConcurrentModificationException?

Если это ArrayList не бросит ConcurrentModificationException ... , но это не делает его поточно-!!

Например, если другой поток вызывает set(obj, pos) на someCollection, пока ваш поток вызывает этот конструктор, то содержимое для вновь созданного ArrayList непредсказуемый.

1

ConcurrentModificationException - не единственный признак того, что что-то не является потокобезопасным. Если, например, в методе # 1 someCollection также является ArrayList, у вас никогда не будет исключения ConcurrentModificationException (см. Код). Однако целостность данных подвержена риску - если исходный ArrayList изменяется во время его копирования, только некоторые из изменений могут отражать в копии (и не обязательно самые старые изменения!).

Иными словами, атомарность не гарантируется (если только источник специально не разработан для нее, например, с CopyOnWriteArrayList).

EDIT: на самом деле, если вы не правильно синхронизируете свои потоки, копируете массив с одним потоком, а другой поток обновлений ссылок в нем проблематичен в модели памяти Java и теоретически может привести к непредсказуемому поведению.

+0

Это не теоретическая проблема: непредсказуемое поведение * будет * происходить. Единственная точка неопределенности - это то, как часто это произойдет. –

+0

@ Stephen: вы правы, но перепрограммировать ошибки, не связанные с порядком, чрезвычайно сложно. Я нашел только один пример в следующей статье: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html –

+0

Трудно воспроизвести! = Теоретический. Кроме того, несколько примеров, описанных на веб-страницах! = Теоретические. :-) –

5

Thread safe и ConcurrentModificationException - это разные концепции. Объектом, защищенным потоком, является тот, где несколько потоков могут одновременно вызывать свои методы, и данные, хранящиеся внутри объекта, гарантируют, что они не будут повреждены (пример: http://thejavacodemonkey.blogspot.com/2007/08/making-your-java-class-thread-safe.html).A ConcurrentModificationException возникает, когда, например, вы находитесь в середине итерации через коллекцию, и коллекция изменяется. Изменение может происходить из другого потока или того же потока.

В вашем конструкторе, если другой поток изменяет someCollection, пока ваш конструктор копирует его, это может привести к неопределенному поведению (т. Е. Повреждению данных в вашей новой коллекции, поскольку коллекции не являются потокобезопасными) или ConcurrentModificationException (например, если коллекция обнаруживает параллельную модификацию, но это не гарантируется, потому что она не является потокобезопасной ... :-)

Если ваш конструктор будет принимать Collection<Object>, вам необходимо убедиться, что другие потоки не изменять коллекцию до тех пор, пока не вернется ваш конструктор.

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

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