我们知道,ArrayList,LikedList,HashMap都是线程不安全的容器
同步容器:Vector,HashTable,SynchronizedList是线程安全的,因为里面加了synchronized同步,所以这样的容器也叫同步容器,这样的容器在高并发的情况下性能会很低,相当于并发的线程串行化执行了。
JUC提供了一些并发容器:今天来看看CopyOnWriteArrayList
CopyOnWriteArrayList适合读多写少的场景,对实时性要求不高的业务可以用。
先来看一段代码:
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList();
// CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
list.add("fox");
list.add("mark");
list.add("周瑜");
new Thread(()->list.add("诸葛")).start();
for (String str : list) {
System.out.println(str);
}
}
}
遍历ArrayList的时候,对ArrayList进行set、add、remove都会报错:java.util.ConcurrentModificationException
将ArrayList改成CopyOnWriteArrayList就不会有这个问题。
为什么呢?
以add(o)方法为例,看看源码:add属于写操作,是需要上独享锁的,这里的锁可能根据jdk版本的不同而不同,jdk11用的是synchronized关键字, jdk8用的是可重入锁。
底层是写时复制机制。
CopyOnWriteArrayList底层用的是数组来存储数据,流程图如下:
总结来说就是新建了一个新的数组,数据和原来数组元素一样,读(get)是不加锁的而且读的是原来的旧数组,写操作是操作的新数组。
CopyOnWriteArrayList的缺点:不适合大量数据量的场景(如数据量太大会一起OOM或者FullGC);只能保证数据的最终一致性。
应用:nacos缓存注册信息,黑名单的判断等等。