2014-10-17 48 views
16

我需要一个ArrayList状结构让刚刚以下操作并发的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的副本,这一个不是。

+2

你可能想尝试Clojure的['PersistentVector'](https://github.com/clojure/clojure/blob/master/src/jvm/ clojure/lang/PersistentVector.java),它也是写时复制,但不是一个天真的单片阵列;而是一个宽而浅的数组树。在我链接到的源代码的末尾,您会发现测试代码。 – 2014-10-17 11:34:21

+1

@skaffman您可以在关闭它之前仔细阅读这个问题吗?查看我的更新。 – maaartinus 2014-10-17 12:32:15

+0

@MarkoTopolnik这会工作,但代码看起来很奇怪。它还增加了我不关心的操作所需的一些开销。顺便说一句,你可以投票重新开放吗?恕我直言,MOD在阅读时有点太快。 – maaartinus 2014-10-17 13:24:46

回答

4

对于您的情况,您可以使用ReadWriteLock来访问支持列表,这允许多个线程读取您的列表。只有一个Thread需要写入权限时,所有reader-Thread都必须等待操作完成。的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(); 
     } 
    } 
+0

这很好,但我不能使用'ArrayList'的迭代器,因为它会抛出'ConcurrentModificationException'。我想,有一个简单的解决方法。 – maaartinus 2014-10-18 20:15:34

+0

如果您想在迭代过程中添加元素,则无法避免出现'ConcurrentModificationException'。迭代时想象下一个元素在索引5处,但同时另一个线程在索引1处添加一个元素,现在会发生什么? 唯一的出路是创建List的immutalbe副本以获取独立迭代器或滥用writeLock来锁定迭代循环块。 – Chriss 2014-10-18 20:38:52

+0

“*现在会发生什么*” - 确切地说,这不会发生,因为不会有这样的操作。但追加或替换元素可以,然后我只是不在乎。获得旧的价值是好的,所以它获得了新的价值。 – maaartinus 2014-10-18 21:55:47