Вы можете использовать ReadWriteLock , чтобы разрешить нескольким потокам параллельно выполнять операции добавления в списке поддержки, но только один поток для создания моментального снимка.Пока снимок готовится, все остальные запросы на добавление и снимок удерживаются.
ReadWriteLock поддерживает пару связанных блокировок, одну для операций только для чтения и одну для записи.Блокировка чтения может удерживаться одновременно несколькими потоками считывателя, если нет писателей.Блокировка записи является эксклюзивной.
class CopyOnReadList<T> {
// free to use any concurrent data structure, ConcurrentLinkedQueue used as an example
private final ConcurrentLinkedQueue<T> items = new ConcurrentLinkedQueue<T>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock shared = rwLock.readLock();
private final Lock exclusive = rwLock.writeLock();
public void add(T item) {
shared.lock(); // multiple threads can attain the read lock
// try-finally is overkill if items.add() never throws exceptions
try {
// Add item while holding the lock.
items.add(item);
} finally {
shared.unlock();
}
}
public List<T> makeSnapshot() {
List<T> copy = new ArrayList<T>(); // probably better idea to use a LinkedList or the ArrayList constructor with initial size
exclusive.lock(); // only one thread can attain write lock, all read locks are also blocked
// try-finally is overkill if for loop never throws exceptions
try {
// Make a copy while holding the lock.
for (T t : items) {
copy.add(t);
}
} finally {
exclusive.unlock();
}
return copy;
}
}
Редактировать:
Блокировка чтения-записи названа так, потому что она основана на проблеме читателей-писателей, а не на том, как она используется,Используя блокировку чтения-записи, мы можем сделать так, чтобы несколько потоков достигли блокировок чтения, но только один поток достигал блокировки записи исключительно.В этом случае проблема обратная - мы хотим, чтобы несколько потоков писали (добавляли) и читали только потоки (делали снимок).Итак, мы хотим, чтобы несколько потоков использовали блокировку чтения, даже если они на самом деле мутируют.Только поток исключительно делает снимок, используя блокировку записи, хотя снимок только читает.Исключительно означает, что во время создания снимка никакие другие запросы на добавление или снимок не могут одновременно обслуживаться другими потоками.
Как указывало @PeterLawrey, очередь Concurrent будет сериализовать записи, хотя блокировки будут использоваться дляминимальная продолжительность, насколько это возможно.Мы можем использовать любую другую параллельную структуру данных, например ConcurrentDoublyLinkedList .Очередь используется только в качестве примера.Основная идея - использование блокировок чтения-записи.