CopyOnWriteArrayList 有一个性能缺陷,因为它会在写操作时创建列表的底层数组的副本。数组复制使写操作变慢。可能是,CopyOnWriteArrayList 有利于使用具有高读取率和低写入率的 List。
最终,我开始使用 java.util.concurrent.locks,ReadWriteLock 编写自己的实现。我只是通过维护对象级别的 ReadWriteLock 实例,并在读操作中获得读锁并在写操作中获得写锁来完成我的实现。代码看起来像这样。
public class ConcurrentList< T > implements List< T >
{
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final List< T > list;
public ConcurrentList( List<T> list )
{
this.list = list;
}
public boolean remove( Object o )
{
readWriteLock.writeLock().lock();
boolean ret;
try
{
ret = list.remove( o );
}
finally
{
readWriteLock.writeLock().unlock();
}
return ret;
}
public boolean add( T t )
{
readWriteLock.writeLock().lock();
boolean ret;
try
{
ret = list.add( t );
}
finally
{
readWriteLock.writeLock().unlock();
}
return ret;
}
public void clear()
{
readWriteLock.writeLock().lock();
try
{
list.clear();
}
finally
{
readWriteLock.writeLock().unlock();
}
}
public int size()
{
readWriteLock.readLock().lock();
try
{
return list.size();
}
finally
{
readWriteLock.readLock().unlock();
}
}
public boolean contains( Object o )
{
readWriteLock.readLock().lock();
try
{
return list.contains( o );
}
finally
{
readWriteLock.readLock().unlock();
}
}
public T get( int index )
{
readWriteLock.readLock().lock();
try
{
return list.get( index );
}
finally
{
readWriteLock.readLock().unlock();
}
}
//etc
}
观察到的性能改进是显着的。
10 个线程 5000 次读取 + 5000 次写入(读写比率为 1:1)的总时间为
- ArrayList - 16450 ns(不是线程安全的)
- 并发列表 - 20999 ns
- 矢量 -35696 ns
- CopyOnWriteArrayList - 197032 ns
请点击此链接以获取有关用于获得上述结果的测试用例的更多信息
但是,为了避免在使用 Iterator 时出现 ConcurrentModificationException,我只是创建了当前 List 的副本并返回了它的迭代器。这意味着这个列表不会返回,并且可以修改原始列表的迭代器。好吧,对我来说,这暂时还可以。
public Iterator<T> iterator()
{
readWriteLock.readLock().lock();
try
{
return new ArrayList<T>( list ).iterator();
}
finally
{
readWriteLock.readLock().unlock();
}
}
经过一番谷歌搜索后,我发现 CopyOnWriteArrayList 具有类似的实现,因为它不返回可以修改原始列表的迭代器。Javadoc 说,
返回的迭代器提供了构造迭代器时列表状态的快照。遍历迭代器时不需要同步。迭代器不支持 remove 方法。