CopyOnWriteArrayList原理

简介: 文章主要讨论了CopyOnWriteArrayList的工作原理。CopyOnWriteArrayList通过“写时复制”的策略和ReentrantLock锁来保证线程安全性,适用于读多写少的场景;该实现牺牲了一定的写入性能(因为每次写入都需要复制整个数组),但显著提高了读取性能;在多线程环境中,特别是读取操作远多于写入操作时,CopyOnWriteArrayList是一个非常有效的选择。

本文知识需要理解之前的文章ReentrantLock知识才能更好的理解CopyOnWriteArrayList的原理,因为它是基于ReentrantLock来实现并发安全的。

ArrayList不是一个线程安全的容器,多线程并发操作会造成数据不一致,Jdk官方提供了解决方案:CopyOnWriteArrayList,它是一个并发安全的容器,从名字可以看出来,如果要修改这个集合的时候,会拷贝一份出来修改,然后替换原理的数组。 下面我们通过查看源码来验证是否是这样实现的。

add方 法:

//定义了一把全局锁final transient ReentrantLock lock = new ReentrantLock();​//一个volatile修饰的数组,保证多线程之间可见。    /** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;    //add方法为例子    都会先获取锁,然后拷贝一份数据进行修改,最后再替换原数组 其它修改集合的方法也类似。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();        }    }
AI 代码解读

通过add源码看出,操作数组时先获取全局锁,再从旧数组拷贝数据到新数组,最后将新数组替换老数组,这样不会直接修改老数组里的数据。

get方法:

    private E get(Object[] a, int index) {
           return (E) a[index];    }​    /**     * {@inheritDoc}     *     * @throws IndexOutOfBoundsException {@inheritDoc}     */    public E get(int index) {
           return get(getArray(), index);    }​    final Object[] getArray() {
           return array;    }
AI 代码解读

通过get方法看出,get方法是直接读数组里的数据,也符合

CopyOnWriteArrayList类名的意思,只有写才会拷贝旧数组。

迭代器源码实现:

static final class COWIterator<E> implements ListIterator<E> {
           /** 快照数据 Snapshot of the array */        private final Object[] snapshot;        /** Index of element to be returned by subsequent call to next.  */        private int cursor; }  public Iterator<E> iterator() {
           return new COWIterator<E>(getArray(), 0); }
AI 代码解读

迭代器类使用了快照技术,不直接迭代原数组,因此不会有ConcurrentModificationException异常。

CopyOnWriteArrayList应用举例:

public class DriverManager {    // Jdbc驱动存储到CopyOnWriteArrayList    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();}
AI 代码解读

总结一下:

CopyOnWriteArrayList 是线程安全的并发容器,多线程操作列表的场景可以使用,读多写少,有以下两个特点:

1、对数组的写操作加锁,读操作不加锁;

2、通过加锁 + 数组拷贝(空间换时间)+ volatile 来保证线程安全;

CopyOnWriteArrayList还是比较简单的,它利用了ReentrantLock技术,

java.util.concurrent并发包中有很多并发安全的容器都借助了

ReentrantLock技术来实现,比如阻塞队列BlockingQueue,闭锁CountDownLatch等等,后面文章将会一一来分析。

目录
打赏
0
0
0
0
41
分享
相关文章
Java多线程基础-18:线程安全的集合类与ConcurrentHashMap
如果这些单线程中的集合类确实需要在多线程中使用,该怎么办呢?思路有两个: 最直接的方式:使用锁,手动保证。如多个线程修改ArrayList对象,此时就可能有问题,就可以给修改操作进行加锁。但手动加锁的方式并不是很方便,因此标准库还提供了一些线程安全的集合类。
162 4
CopyOnWriteArrayList底层原理全面解析【建议收藏】
CopyOnWriteArrayList是Java中的一个线程安全的集合类,是ArrayList线程安全版本,主要通过Copy-On-Write(写时复制,简称COW)机制来保证线程安全。 Copy-On-Write机制核心思想:向一个数组中添加数据时,不直接操作原始数组,而是拷贝原始数组生成一份原始数组副本,将需要添加的数据添加到原始数组副本中,操作完成后再用原始数组副本直接替换原始数组,从而保证多个线程同时操作原始数组时的线程安全。
ConcurrentHashMap概念与深入理解
ConcurrentHashMap是Java集合框架中的一个重要类,它是线程安全的哈希表实现。相比于普通的HashMap,ConcurrentHashMap在多线程环境中提供了更好的性能和可靠性。本文将详细介绍ConcurrentHashMap的概念、特点以及其内部实现原理。
263 0
java并发原理实战(5)--线程安全性问题和synchronized原理理解
java并发原理实战(5)--线程安全性问题和synchronized原理理解
java并发原理实战(5)--线程安全性问题和synchronized原理理解
Java并发编程解析,ConcurrentHashMap理解
Java并发编程解析,ConcurrentHashMap理解
142 0
Java并发编程解析,ConcurrentHashMap理解
HashMap的底层原理和线程安全的替代方案
HashMap是基于哈希表的Map接口的非同步实现,常见的数据结构有`堆栈、队列、数组、链表和红黑树`,Java中最基本的数据结构有两种,一种是`数组`,一种是`引用`。可以说其他所有的数据结构都可以从这两个最基本结构构造而来,当然HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即**数组**和**链表**的结合体,所以说HashMap的底层其实就是一个数据结构,被称为`哈希表结构`,数组中的每一项又是一个链表。当新建一个HashMap的时候,内部就会初始化一个数组。
832 0
HashMap的底层原理和线程安全的替代方案

热门文章

最新文章