CopyOnWriteArrayList
目录CopyOnWriteArrayList诞生记
- 代替Vector和SynchronizedList,就像ConcurrentHashMap代替SynchronizedMap的原因一样
- Vector和SynchronizedList的锁的粒度太大,并发效率相对比较低,并且迭代时无法编辑
- Copy-On-Write并发容器还包括CopyOnWriteArraySet,用来替代同步Set
CopyOnWriteArrayList使用场景
- 读操作可以尽可能地快,而写即使慢一点也没有太大关系。
- 读多写少:黑名单,每日更新;监听器:迭代操作远多于修改操作
CopyOnWriteArrayList读写操作
- 回顾读写锁:读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)
- 读写锁规则的升级:读取是完全不用加锁的,并且更厉害的是,写入也不会阻塞读取操作。只要写入和写入之间需要进行同步等待。
public class CopyOnWriteArrayListDemo1 {
public static void main(String[] args) {
// ArrayList<String> list = new ArrayList<>();
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println("list is "+list);
String next = iterator.next();
System.out.println(next);
if(next.equals("2")){
list.remove("5");
}
}
}
}
实现原理
ArryaList之所以报错原因是
final void checkForComodification() {
// expectedModCount 为创建迭代器时的值
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
- CopyOnWrite的含义:拷贝一份数据到新的内存,将指针指向新的地址
- 创建新副本、读写分离
- “不可变”原理
- 迭代的时候可能会出现数据过期问题
缺点
- 数据一致性问题:CopyOnWrite容器只能保证数据的最终-致性,不能保证数据的实时一致性。所以如果你希望写入的数据,马上能读到,请不要使用CopyOnWrite容器:
- 内存占用问题:因为CopyOnWrite的写是复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存。
源码分析
- 数据结构
- get():可以看到,get方法直接获取元素信息
- set():加Lock锁,同时拷贝一份新的数组,将set的值放置新数组最后一个元素位置