并发容器之CopyOnWrite

本文介绍了CopyOnWrite容器的工作原理,它是写时复制的并发容器,适用于读多写少场景。CopyOnWriteArrayList作为示例,详细阐述了其如何在并发操作中实现线程安全,以及其效率高但存在数据实时性和内存占用较大的特点。
摘要由CSDN通过智能技术生成

CopyOnWrite容器

什么是CopyOnWrite容器呢?CopyOnWrite容器是一个写时复制的容器。在向容器中添加元素时,不会直接向当前容器中添加,而是将当前容器进行copy,复制出一个新的容器,然后往新的容器中添加元素,添加完元素之后,再将容器的引用指向新的容器。使得我们可以对CopyOnWrite容器进行并发的读而不需要加锁,采用了读写分离的思想,写时复制的策略

使用的场景是读多写少的时候使用,如redis、Linux的文件管理系统等

基本思路

  • 当读取共享数据时,直接读取,不需要有其他操作
  • 当写共享数据时,将旧数据复制出来一份作为新数据,只修改新数据,修改完新数据后将新数据的引用赋值给原来数据的引用,在写数据的过程中,所有读取共享数据都是读的旧数据

以CopyOnWriteArrayList为例

CopyOnWriteArrayList

CopyOnWriteArrayList是同步List的并发替代品,是java并发包java.util.concurrent中提供的用于并发操作且线程安全的ArrayList,可以提供更好的并发性,并且避免了在迭代期间对容器加锁和复制,在每次修改的时,会创建一个新的容器拷贝,以此来实现可变性

// 存放具体的元素
private transient volatile Object[] array;
// 独占锁用来保证同时只有一个线程对array进行修改
final transient ReentrantLock lock = new ReentrantLock();

实际是对底层数组的复制操作

    public E set(int index, E element) {
      // 获取独占锁,写入时加锁,保证只有一个线程在写,防止多线程时copy多份副本
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
          // 获取array
            Object[] elements = getArray();
            E oldValue = get(elements, index);
      // 值修改时
            if (oldValue != element) {
                int len = elements.length;
               // 根据原来的数组拷贝一个新的数组
                Object[] newElements = Arrays.copyOf(elements, len);
               // 对新的数组调整赋值
                newElements[index] = element;
               // 原数组的引用指向新数组,替换掉之前的数组
                setArray(newElements);
            } else {// 值没有修改
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

每次容器改变对于基础数组的复制也是有一定开销的,特别是当容器较大时,所以该种方式比较适合于读取操作的次数远大于修改操作的次数时才适用

但是对于获取操作并不会进行加锁,而是直接进行获取

final Object[] getArray() {
  return array;
}

public E get(int index) {
    return get(getArray(), index);
}

private E get(Object[] a, int index) {
 return (E) a[index];
}

所以可能在进行读取的时候获取到的数据并不准确,这是写时复制策略产生的弱一致性问题

优缺点

优点
  • 效率高,读写操作不是同一份数据,在进行读和写时不需要阻塞其他来读取的线程
  • 保证最终一致性,读和写操作的不是同一份数据,可以保证读数据的操作不会读到写了一半的数据
缺点
  • 数据实时性差,在写操作完成之前之前都是读取旧数据
  • 内存占用大,有复制操作,将旧数据复制出来一份作为新数据,会占用两份内存,以时间换空间

https://zhhll.icu/2021/多线程/并发容器/2.CopyOnWrite容器/

本文由 mdnice 多平台发布

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾光师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值