Это зависит от того, как вы хотите ограничить параллелизм.Самым простым способом, вероятно, является использование CopyOnWriteArrayList
.Когда вы извлекаете из него итератор, этот итератор будет отображать то, как список выглядел в тот момент, когда был создан итератор - последующие изменения не будут видны итератору.Плюс в том, что он может справиться с большим количеством споров, недостаток в том, что добавление новых предметов довольно дорого.
Другой способ - это блокировка, возможно, самый простой способ - обернуть список с помощью Collections.synchronizedList
и синхронизировать список при итерации.
Третий способ -используя какой-то BlockingQueue
и передать новые элементы рабочим.
Редактировать: Поскольку в ОП указано, что требуется только моментальный снимок, CopyOnWriteArrayList
вероятно, лучшая альтернатива из коробки.Альтернативой (для более дешевого добавления, но более дорогостоящего чтения) является просто создание копии synchronizedList
, когда необходим переход (копирование при чтении, а не копирование при записи):
List<Foo> originalList = Collections.synchronizedList(new ArrayList());
public void mainThread() {
while(true)
originalList.add(getSomething());
}
public void workerThread() {
while(true) {
List<Foo> copiedList;
synchronized (originalList) {
copiedList = originalList.add(something);
}
for (Foo f : copiedList) process(f);
}
}
Редактировать: Если подумать, версия для копирования при чтении может немного упростить, чтобы избежать всех synchronized
блоков:
List<Foo> originalList = Collections.synchronizedList(new ArrayList());
public void mainThread() {
while(true)
originalList.add(getSomething());
}
public void workerThread() {
while(true) {
for (Foo f : originalList.toArray(new Foo[0]))
process(f);
}
}
Редактировать 2: Вот простая оболочка для списка копирования при чтении, в которой не используются никакие помощники, и которая старается быть как можно более мелкой в блокировке (я намеренно сделал ее несколько чрезмерной, граничащей с неоптимальной, чтобы продемонстрироватьгде требуется блокировка):
class CopyOnReadList<T> {
private final List<T> items = new ArrayList<T>();
public void add(T item) {
synchronized (items) {
// Add item while holding the lock.
items.add(item);
}
}
public List<T> makeSnapshot() {
List<T> copy = new ArrayList<T>();
synchronized (items) {
// Make a copy while holding the lock.
for (T t : items) copy.add(t);
}
return copy;
}
}
// Usage:
CopyOnReadList<String> stuff = new CopyOnReadList<String>();
stuff.add("hello");
for (String s : stuff.makeSnapshot())
System.out.println(s);
Обычно, когда вы блокируете, когда вы:
- ... добавляете элемент в список.
- ... переберите список, чтобы сделать его копию.