CopyOnWriteArrayList,我对这样的第一印象就是在讲多线程的时候,例子中使用到过这个。
JavaDoc翻译
一种线程安全的变体,ArrayList其中的所有可变操作(add,set等)都通过对基础数组进行全新复制来实现。当遍历操作远远超过修改时,它可能比替代方法更有效,并且在您无法或不想同步遍历而又需要防止并发线程之间的干扰时很有用。迭代器方法在创建迭代器时使用对数组状态的引用。该数组在迭代器的生命周期内永不更改,因此不可能发生干扰,并且保证迭代器不会抛出ConcurrentModificationException。自创建迭代器以来,该迭代器将不会反映对该列表的添加,删除或更改。元变化的迭代器操作本身(remove,set,和 add)不被支持。这些方法抛出 UnsupportedOperationException。
允许所有元素,包括null。
内存一致性影响:与其他并发集合一样,在将对象放入CopyOnWriteArrayList 发生之前的线程中的 操作在从CopyOnWriteArrayList另一个线程中访问或删除该元素之后的操作。
此类是 Java Collections Framework的成员 。
CopyOnWriteArrayList的体系图
可以很明显的看出这是List接口。也就是说所有的操作都是和List中一样的,但是我们的需要知道的是为什么它是线程安全的。
当我们使用add接口的时候,很明显的可以看出,在CopyOnWriteArrayList中它会添加到最后,点开他的源码可以看到。
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }
在代码中他和别的不一样,是通过使用显式lock锁的方法来实现的。先放一个空然后再设置,这样的话其实就是多个原子性的操作进行组合。
众所周知,Lock是一个接口,有不同的实现类,基于AbstractQueuedSyncronizer(AQS)实现,ReentrantLock(可重入锁),ReadWriteLock(读写锁), ReentrantReadWriteLock(可重入读写锁,state高16位标识读锁,低16位标识写锁)等。来这边使用的是可重入锁(就是一个线程在获取了锁之后,再次去获取了同一个锁,这时候仅仅是把状态值进行累加)。
但是在开发中我们宁可自己去写,也很少使用这个,其实是有原因的。
首先独占锁效率低,其次写线程获取到锁,其他线程包括读线程阻塞。
但是它的思想其实是很好的:读写分离,读和写分开;最终一致性;使用另外开辟空间的思路,来解决并发冲突
这三个可以很好的解决我们遇到的问题。
写到这里,感觉不知道再写一些什么了......
理解这些其实就是要理解Lock,就可以很好的明白了。