我需要一个类似ArrayList的结构,只允许以下操作
get(int index)
add(E element)
set(int index, E element)
iterator()
由于在许多地方使用迭代器,因此使用Collections#synchronizedList
会容易出错。该列表可以增长到几千个元素并且会被大量使用,所以我很确定CopyOnWriteArrayList
会太慢。我会从它开始,以避免过早的优化,但我敢打赌它不会很好地工作。
大多数访问都是单线程读取。所以我问这个适当的数据结构是什么。
我虽然将synchronizedList
包装在提供同步迭代器的东西中会有所作为,但由于ConcurrentModificationException
它不会成功。对于并发行为,我显然需要后续读取和迭代器可以看到所有更改。
迭代器不必显示一致的快照,它可能会或不会通过set(int index, E element)
看到更新,因为此操作仅用于替换具有更新版本的项目(包含一些附加信息) ,这与迭代器的用户无关)。这些项目是完全不可变的。
我清楚地说明为什么CopyOnWriteArrayList
不会这样做。由于缺少索引访问权限,ConcurrentLinkedQueue
是不可能的。我只需要几个操作而不是完全成熟的ArrayList
。因此,除非任何与Java并发列表相关的问题与this question重复,否则此问题不是。{/ p>
答案 0 :(得分:7)
在您的情况下,您可以使用ReadWriteLock访问支持的列表,这允许多个线程读取您的列表。只有当一个线程需要写访问时,所有读者 - 线程必须等待操作完成。 JavaDoc明确指出:
ReadWriteLock维护一对关联的锁,一个用于 只读操作和写操作。可以保持读锁定 同时由多个读者线程,只要没有 作家。写锁是独占的。
以下是一个示例:
public class ConcurrentArrayList<T> {
/** use this to lock for write operations like add/remove */
private final Lock readLock;
/** use this to lock for read operations like get/iterator/contains.. */
private final Lock writeLock;
/** the underlying list*/
private final List<T> list = new ArrayList();
{
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
readLock = rwLock.readLock();
writeLock = rwLock.writeLock();
}
public void add(T e){
writeLock.lock();
try{
list.add(e);
}finally{
writeLock.unlock();
}
}
public void get(int index){
readLock.lock();
try{
list.get(index);
}finally{
readLock.unlock();
}
}
public Iterator<T> iterator(){
readLock.lock();
try {
return new ArrayList<T>( list ).iterator();
//^ we iterate over an snapshot of our list
} finally{
readLock.unlock();
}
}