CopyOnWriteArrayList使用以及原理分析

文章目录

  • 一、CopyOnWriteArrayList的简介
  • 二、CopyOnWriteArrayList类的继承关系
    • 1、Iterable接口:
    • 2、Collection接口:
    • 3、List接口:
    • 4、Cloneable接口:
    • 5、Serializable接口:
    • 6、RandomAccess接口:
  • 三、CopyOnWriteArrayList的基本使用
    • 1. 使用场景:
      • 1.1、读多写少的情况:
      • 1.2、数据一致性要求较低:
      • 1.3、需要遍历操作:
    • 2. 代码实现:
    • 3. 运行结果:
    • 4. 案例分析:
  • 四、CopyOnWriteArrayList的优缺点
    • 1. 优点:
      • 1.1、线程安全:
      • 1.2、并发读取性能好:
      • 1.3、可用于读多写少的场景:
    • 2. 缺点:
      • 2.1、内存占用高:
      • 2.2、写操作的延迟高:
      • 2.3、不适用于迭代器的弱一致性需求:
    • 3. 总结:
  • 五、底层原理分析
    • 1. add方法
    • 2. get方法

一、CopyOnWriteArrayList的简介

copyonwriteArrayList

  1. CopyOnWriteArrayList是Java中的一种并发集合类,它实现了List接口,并且是线程安全的。它的特点是在进行写操作时,会创建一个新的副本来进行操作,而不是直接修改原有的集合。

  2. CopyOnWriteArrayList的实现机制是在写操作时,先将原有的集合进行复制,然后对新的副本进行修改操作,最后将修改后的副本替换原有的集合。这样可以保证在写操作时不会影响到正在进行读操作的线程,保证了读写的并发性。

  3. 由于每次写操作都会复制整个集合,所以CopyOnWriteArrayList的写操作性能较低,适用于读操作较多而写操作较少的场景。它适合用于一些读多写少的并发场景,比如缓存、观察者模式等。

  4. 需要注意的是,CopyOnWriteArrayList虽然是线程安全的,但是它的迭代器并不支持修改操作,如果需要对集合进行修改,需要使用特殊的方法来操作。此外,由于每次写操作都会创建一个新的副本,所以CopyOnWriteArrayList占用的内存较大,需要根据具体场景进行权衡。

二、CopyOnWriteArrayList类的继承关系

1、Iterable接口:

CopyOnWriteArrayList类实现了Iterable接口,使得它可以被迭代遍历。通过实现iterator()方法,可以获取一个迭代器,用于遍历集合中的元素。

2、Collection接口:

CopyOnWriteArrayList类继承了AbstractCollection类,间接实现了Collection接口。通过实现Collection接口,CopyOnWriteArrayList类获得了一些常用的集合操作方法,比如判断是否包含某个元素、计算集合的大小等。

3、List接口:

CopyOnWriteArrayList类间接实现了List接口,通过继承AbstractList类实现了List接口的一些方法。List接口定义了有序的集合,CopyOnWriteArrayList类通过继承List接口,可以按照索引进行访问和操作集合中的元素。

4、Cloneable接口:

CopyOnWriteArrayList类实现了Cloneable接口,使得它可以进行克隆操作。通过实现clone()方法,可以创建CopyOnWriteArrayList对象的副本。

5、Serializable接口:

CopyOnWriteArrayList类实现了Serializable接口,使得它可以进行序列化操作。通过实现writeObject()和readObject()方法,可以将CopyOnWriteArrayList对象转换为字节流,并进行存储或传输。

6、RandomAccess接口:

CopyOnWriteArrayList类实现了RandomAccess接口,表示它可以高效地随机访问元素。RandomAccess接口是一个标记接口,用于标识实现该接口的类可以通过索引进行快速随机访问,CopyOnWriteArrayList类通过实现该接口,表明它可以高效地随机访问元素。

三、CopyOnWriteArrayList的基本使用

1. 使用场景:

CopyOnWriteArrayList的主要使用场景是在多线程环境下,需要读多写少的情况下使用。

由于CopyOnWriteArrayList是线程安全的,它通过在写操作时创建一个新的数组来实现并发安全。在写操作时,原有数组不发生改变,而是将写操作应用于新的数组。这样就避免了读操作和写操作的冲突,保证了线程安全。

CopyOnWriteArrayList适用于以下场景:

1.1、读多写少的情况:

由于写操作会创建新的数组,因此写操作的开销较大。如果写操作很频繁,会导致性能下降。因此,在读多写少的情况下,CopyOnWriteArrayList能够提供较好的性能。

1.2、数据一致性要求较低:

由于写操作不会影响原来的数组,读操作总是读取最新的数组。因此,如果对数据的实时性要求不高,可以使用CopyOnWriteArrayList。

1.3、需要遍历操作:

CopyOnWriteArrayList适合于需要频繁进行遍历操作的场景。由于读操作不需要加锁,所以可以并发地进行遍历操作,提高了遍历的效率。

需要注意的是,CopyOnWriteArrayList适用于静态数据,即数据一旦初始化就不会修改的情况。如果数据需要频繁修改,则不适合使用CopyOnWriteArrayList。此外,由于每次写操作都会创建一个新的数组,占用内存较大,所以不适合数据量过大的情况。

2. 代码实现:

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListDemo {public static void main(String[] args) {// 创建一个CopyOnWriteArrayList对象CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();// 添加元素list.add(1);list.add(2);list.add(3);// 创建一个线程用于遍历元素Thread iteratorThread = new Thread(() -> {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});// 创建一个线程用于修改元素Thread modifyThread = new Thread(() -> {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}list.add(4);list.remove(1);});// 启动两个线程iteratorThread.start();modifyThread.start();}
}

3. 运行结果:

运行结果

4. 案例分析:

  1. 在上面的代码中,我们首先创建了一个CopyOnWriteArrayList对象,并向其中添加了一些元素。然后,我们创建了两个线程:一个线程用于遍历元素,另一个线程用于修改元素。在遍历线程中,我们使用iterator()方法获取迭代器,并通过调用next()方法来遍历元素。在修改线程中,我们在添加和删除元素之间添加了一个延迟,以便观察到遍历线程的输出结果。

  2. 由于CopyOnWriteArrayList是线程安全的,即使在遍历的同时进行元素的修改,也不会抛出ConcurrentModificationException异常。这是因为在修改操作时,CopyOnWriteArrayList会创建一个新的数组来存储修改后的元素,而原来的数组不会受到影响。因此,遍历线程可以继续使用原来的数组进行遍历,保证了遍历的安全性。

四、CopyOnWriteArrayList的优缺点

1. 优点:

1.1、线程安全:

CopyOnWriteArrayList是线程安全的,多个线程可以同时读取列表中的数据而无需额外的同步机制。

1.2、并发读取性能好:

由于读操作不需要加锁,多个线程可以同时读取,提高了并发读取的性能。

1.3、可用于读多写少的场景:

适用于读操作频繁,写操作较少的场景。

2. 缺点:

2.1、内存占用高:

每次写操作都会创建一个新的数组,导致内存占用较高。如果写操作频繁,会消耗大量的内存。

2.2、写操作的延迟高:

由于每次写操作都会复制整个数组,写操作的执行时间较长,可能会导致写操作的延迟较高。

2.3、不适用于迭代器的弱一致性需求:

CopyOnWriteArrayList的迭代器是弱一致性的,即迭代器在创建之后不会反映后续的修改操作。这可能会导致迭代器在遍历过程中出现数据不一致的情况。

3. 总结:

综上所述,CopyOnWriteArrayList适用于读多写少的场景,对于需要高并发性能和线程安全的读操作非常有用。然而,它的内存占用高和写操作的延迟高可能会影响性能,在弱一致性需求较高的情况下也不适合使用。

五、底层原理分析

1. 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();}}
  1. 获取一把独占多lock ,加锁
  2. 获取原数组长度,创建一个原数组长度+1的新数组
  3. 利用Arrays.copyOf将元素进行复制到新的数组
  4. 将添加的元素设置到新开辟数组的最后一个位置
  5. 修改老的数组的引用指向
  6. 释放锁

2. get方法

    public E get(int index) {return get(getArray(), index);}private E get(Object[] a, int index) {return (E) a[index];}
  1. 根据下标获取指定位置元素,可以看到读不加锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18401.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 匿名对象

一、简介 1.1.含义 没有名字的对象 。以常规的创建对象的方法&#xff1a; AtomicInteger atomicInteger new AtomicInteger(100000);格式&#xff1a; 类名 变量名 new 类名(); 这样就完成了对象的创建。注意&#xff1a;&#xff08;&#xff09;内可以无参数&#xff0c…

前端|项目实操流程|学成在线项目实操

参考视频&#xff1a;黑马程序员前端CSS3基础教程&#xff0c;前端必备基础 目录 &#x1f4da;案例准备工作 &#x1f4da;CSS属性书写顺序 &#x1f407;布局定位属性 &#x1f407;自身属性 &#x1f4da;页面布局整体思路 &#x1f4da;学成在线项目制作 &#x1f4…

DevOps(一)

DevOps 1. DevOps起源1.1 瀑布开发模型1.2 敏捷开发模型 2. DevOps到底是什么&#xff1f;3. DevOps与虚拟化、容器、微服务4. CI/CD是什么 &#xff1f;4.1 CI 持续集成&#xff08;Continuous Integration&#xff09;4.2 CD 持续交付&#xff08;Continuous Delivery&#x…

【Matlab】智能优化算法_亨利气体溶解度优化算法HGSO

【Matlab】智能优化算法_亨利气体溶解度优化算法HGSO 1.背景介绍2.数学模型2.1 亨利定律2.2 HGSO 3.文件结构4.伪代码5.详细代码及注释5.1 Create_Groups.m5.2 Evaluate.m5.3 fun_checkpoisions.m5.4 fun_getDefaultOptions.m5.5 HGSO.m5.6 main.m5.7 sumsqu.m5.8 update_posit…

微信小程序input的placeholder脱离文档流

今天进行真机调试时input的提示词 placeholder脱离了文档流&#xff0c;但是奇怪的是input框没有脱离文档流 如下图所示&#xff1a; 微信开发工具正常&#xff1a; 真机&#xff1a;不正常 脱离文档流 解决方法&#xff1a; <view clas…

【NeRF】相机的内外参是什么?单目相机是如何成像的?

文章目录 【NeRF】相机的内外参是什么&#xff1f;单目相机是如何成像的&#xff1f;1.相机外参2.相机内参 【NeRF】相机的内外参是什么&#xff1f;单目相机是如何成像的&#xff1f; 在做Nerf时对其中的一些原理感到困惑&#xff0c;因而把这些基础理论知识总结下来&#xff…

【工业机器人】用于轨迹规划和执行器分析的机械手和移动机器人模型(MatlabSimulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

1、vscode+cmake c++环境配置

文章目录 1、安装2、开发环境 关于vscode c环境的配置&#xff0c;应该有两种一种是vscodec/c插件&#xff0c;另一种是vscodecmake插件&#xff0c;第一种没太多用过&#xff0c;感觉就像python那样&#xff0c;要写相关配置文件&#xff0c;有自己的一套规则&#xff1b;另一…

【数据结构导论】第 6 章:查找

目录 一、基本概念 二、静态查找表 &#xff08;1&#xff09;顺序表上的查找 —— 顺序查找 ① 过程 ② 算法 ③ 算法分析 &#xff08;2&#xff09;有序表上的查找 —— 二分查找 ① 二分查找思想 ② 二分查找过程 ③ 二分查找算法 ④ 示例 ⑤ 算法分析 &#…

选读SQL经典实例笔记05_日期运算(下)

1. 两个日期之间相差的月份和年份 1.1. DB2 1.2. MySQL 1.3. sql select mnth, mnth/12from ( select (year(max_hd) - year(min_hd))*12 (month(max_hd) - month(min_hd)) as mnthfrom (select min(hiredate) as min_hd, max(hiredate) as max_hdfrom emp) x) y 1.4. Or…

ModaHub魔搭社区:向量数据库Zilliz Cloud的AUTOINDEX教程

目录 创建索引和向量搜索 总结 为满足用户不同需求,Zilliz Cloud 提供 3 种类型的集群 CU——性能型、容量型和经济型。但是,为不同类型 CU 集群中的 Collection 创建索引时,通常需要根据所选择的 CU 类型调整索引参数。为了方便您创建索引,免去调节参数的麻烦,Zilliz C…

stm32 使用CubeIDE 移植RTX5

STM32 使用st的官方开发环境 cubeide &#xff08;eclipse gcc&#xff09;移植 cmsis rtos2 RTX5 实时操作系统 这套环境的主要优势是免费。cubeide免费使用。RTX5 免商业版税&#xff08;已从原keil中剥离出来&#xff0c;现在完全开源免费&#xff09;。 一&#xff0c;环…