微信公众号:放开我我还能学分享知识,共同进步!java
ArrayList 有什么缺点?
你用过线程安全的集合吗?
有,说在哪使用。数组
没有,不过我了解过。安全
那你说说它们的实现。
Vector微信
Vector 自己比较低效,由于它的实现基本就是将 add、get、set 等各类方法加上 synchronized 锁。这就致使了全部并发操做都要竞争同一把锁,一个线程在进行同步操做时,其余线程只能等待,大大下降了并发操做的效率。并发
Collections#SynchronizedList高并发
同步包装器 SynchronizedList 虽然没使用方法级别的 synchronized 锁,可是使用了同步代码块的形式,本质上仍是没有改进。spa
CopyOnWriteArrayList线程
CopyOnWriteArrayList 是一个写时复制的容器,当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy,复制出一个新的容器,而后往新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。这样作的好处是咱们能够对 CopyOnWriteArrayList 进行并发的读,而不须要加锁,由于当前容器不会添加任何元素。因此 CopyOnWriteArrayList 是一种读写分离的容器,适用于读多写少的场景,支持高并发读取。code
CopyOnWriteArrayList 是如何保证写时线程安全的?
使用了 ReentrantLock 独占锁,保证同时只有一个线程对集合进行修改操做。对象
如何理解 CopyOnWrite 思想?
写时复制。就是在写的时候,拷贝一份原对象,只操做拷贝的对象,操做完后再覆盖原对象,保证 volatile 语义。
CopyOnWriteArrayList 的缺点是什么?
CopyOnWriteArrayList 在使用迭代器时是否有什么注意事项?
咱们知道,CopyOnWriteArrayList 是底层使用一种安全失败机制。也就是说,能够在迭代时进行增删改操做。
不过,在迭代器使用时有一个注意事项:迭代器获取的数据取决于迭代器建立的时候,而不是迭代器迭代的时候。
请看下面示例:
public static void main(String[] args) { CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(new Integer[]{1, 2, 3}); ListIterator<Integer> iterator1 = list.listIterator(); list.add(4); ListIterator<Integer> iterator2 = list.listIterator(); iterator1.forEachRemaining(System.out::print); // 123 System.out.println(); iterator2.forEachRemaining(System.out::print); // 1234 }
从上能够看出,iterator1 在添加数据 4 以前就之前建立,那么最终它遍历获取的数据是 123,而 iterator2 在添加完数据 4 以后才建立,那么最终它遍历获取的数据是 1234。
下面我说下缘由。
无论是调用 iterator 方法获取迭代器,仍是调用 listIterator 方法获取迭代器,内部都会返回一个 COWIterator 对象。
进入 COWIterator 构造方法查看,发如今构造方法中会把 array 数组赋值给 snapshot 变量,若是其余线程没有对 CopyOnWriteArrayList 进行增删改的操做,那么 snapshot 就是自己的 array,可是若是其余线程对 CopyOnWriteArrayList 进行了增删改的操做,那么旧的数组会被新的数组给替换掉,可是 snapshot 仍是原来旧的数组的引用。也就是说,当咱们使用迭代器遍历获取数据时,不能保证拿到的数据是最新的。
获取更多最新文章,关注公众号【放开我我还能学】