并发数组列表

2022-09-02 14:00:56

我需要一个类似ArrayList的结构,只允许以下操作

  • get(int index)
  • add(E element)
  • set(int index, E element)
  • iterator()

由于迭代器在许多地方使用,因此使用太容易出错。这个列表可以增长到几千个元素,并且被大量使用,所以我很确定,这会太慢了。我将从它开始以避免过早的优化,但我敢打赌它不会很好地工作。Collections#synchronizedListCopyOnWriteArrayList

大多数访问将是单线程读取。所以我问一下,什么是正确的数据结构。


我虽然将 包装在提供同步迭代器的东西中可以,但它不会,因为.考虑到并发行为,我显然需要后续的读取和迭代器可以看到所有更改。synchronizedListConcurrentModificationException

迭代器不必显示一致的快照,它可能会也可能不会看到更新,因为此操作仅用于将项目替换为其更新版本(包含一些添加的信息,这与迭代器的用户无关)。这些项是完全不可变的。set(int index, E element)


我清楚地说明了为什么不这样做。 是没有问题的,因为它缺少索引访问。我只需要几个操作,而不是一个完全成熟的。因此,除非任何与java并发列表相关的问题是这个问题的重复,否则这个问题不是。CopyOnWriteArrayListConcurrentLinkedQueueArrayList


答案 1

在您的情况下,您可以使用ReadWriteLock访问支持的列表,这允许多个线程读取您的列表。仅当一个线程需要写入访问权限时,所有读取器线程都必须等待操作完成。JavaDoc清楚地表明:

读写锁维护一对关联的锁,一个用于只读操作,一个用于写入。读锁定可以由多个读取器线程同时持有,只要没有写入器即可。写锁定是独占的。

下面是一个示例:

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();
        }
    }

答案 2

推荐