Java - Потоковая безопасность конструкторов ArrayList - PullRequest
8 голосов
/ 21 апреля 2010

Я смотрю на этот кусок кода. Этот конструктор делегирует нативный метод "System.arraycopy"

Поток безопасен? И под этим я подразумеваю, может ли оно когда-либо генерировать исключение 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. Я пытаюсь сделать снимок данных в определенный момент времени. Поэтому, если другой поток пишет в someCollection на полпути через копию, мне все равно, будет ли результат иметь новый объект или нет. Я просто не хочу, чтобы он генерировал исключение ConcurrentModificationException или хуже

Ответы [ 4 ]

7 голосов
/ 21 апреля 2010

Этот конструктор делегирует нативный метод "System.arraycopy"

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

Поток безопасен?

номер

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

Если это ArrayList, он не будет выбрасывать ConcurrentModificationException ... , но это не делает его потокобезопасным !!

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

5 голосов
/ 21 апреля 2010

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

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

5 голосов
/ 21 апреля 2010

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

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

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

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

1 голос
/ 21 апреля 2010

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

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

РЕДАКТИРОВАТЬ: фактически, если предположить, что вы не синхронизируете свои потоки должным образом, копировать массив с одним потоком, в то время как другой поток обновляет ссылки в нем, проблематично в модели памяти Java, и теоретически может привести к непредсказуемому поведению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...