Java基础 -04 List之CopyOnWriteArrayList

news2025/1/12 3:42:45

java集合有蛮多的类型,今天我们以CopyOnWriteArrayList和Vector进行相关介绍。

CopyOnWriteArrayList

CopyOnWriteArrayList是Java集合框架中的一个线程安全的List实现类。它通过在修改操作时创建一个新的副本来实现线程安全性,因此称为"写时复制"。

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。CopyOnWrite容器即写时复制的容器。通俗的理解是当往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

CopyOnWrite并发容器用于读多写少的并发场景。

复制用法导致内存占用可能过高。

保证数据最终一致性,无法保证实时一致性。

在这里插入图片描述
在这里插入图片描述

特点

  • 线程安全:CopyOnWriteArrayList是线程安全的,多个线程可以同时读取列表中的元素,而不需要额外的同步措施。这使得它非常适合在多线程环境下进行读取操作。

  • 写时复制:当有线程对CopyOnWriteArrayList进行修改操作(如添加、删除元素)时,它会创建一个新的副本,并在副本上进行修改操作。这样,其他线程仍然可以在原始列表上进行读取操作,不会受到修改操作的影响。一旦修改完成,新的副本会替换原始列表,以确保修改的一致性。

  • 高效的读取操作:由于读取操作不需要进行同步,CopyOnWriteArrayList在读取操作上具有很高的性能。这使得它非常适合在读多写少的场景中使用。

  • 低效的写入操作:由于每次写入操作都需要创建一个新的副本,CopyOnWriteArrayList在写入操作上的性能相对较低。因此,如果应用程序中有大量的写入操作,可能会影响性能。

  • 迭代器的弱一致性:CopyOnWriteArrayList的迭代器提供了弱一致性的保证。即,迭代器在创建时会获取一个快照,并在迭代过程中遍历该快照。这意味着迭代器不会反映出在迭代过程中对列表所做的修改。

CopyOnWriteArrayList适用于读多写少的场景,特别是在需要保证线程安全性的情况下。它常用于事件监听器列表、缓存等场景,其中读取操作远远超过写入操作。

    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        // 添加元素
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");

        // 遍历元素
        for (String element : list) {
            System.out.println(element);
        }

        // 修改元素
        list.set(1, "Grape");

        // 删除元素
        list.remove("Apple");

        // 遍历元素
        for (String element : list) {
            System.out.println(element);
        }
    }

CopyOnWriteArrayList是如何保证安全的

CopyOnWriteArrayList通过"写时复制"(Copy-On-Write)机制来保证线程安全性。具体来说,当有线程对CopyOnWriteArrayList进行修改操作时,它会创建一个新的副本,并在副本上进行修改操作。这样,其他线程仍然可以在原始列表上进行读取操作,不会受到修改操作的影响。一旦修改完成,新的副本会替换原始列表,以确保修改的一致性。

这种机制的实现步骤如下:

  • 当有线程要对CopyOnWriteArrayList进行修改操作时,它首先会创建一个当前列表的副本。

  • 在副本上进行修改操作,例如添加、删除元素等。

  • 修改完成后,将新的副本替换原始列表,使得其他线程可以看到最新的修改。

通过这种方式,CopyOnWriteArrayList实现了线程安全性,因为每个线程都在自己的副本上进行修改操作,不会影响其他线程的读取操作。这样就避免了传统的同步机制(如锁)带来的竞争和阻塞。

需要注意的是,由于每次修改操作都会创建一个新的副本,CopyOnWriteArrayList在写入操作上的性能相对较低。因此,它更适合于读多写少的场景,特别是在需要保证线程安全性的情况下。

代码层次分析

我们可以从代码层面进行相关分析:

  • 写操作的实现:CopyOnWriteArrayList的写操作(如添加、删除元素)是通过创建一个新的副本来实现的。在添加元素时,会创建一个新的数组,并将原始数组中的元素复制到新数组中,然后在新数组中添加新元素。删除元素时,也是创建一个新的数组,并将原始数组中的元素复制到新数组中,但是不包括要删除的元素。最后,将新数组替换原始数组。

  • 读操作的实现:CopyOnWriteArrayList的读操作是在原始数组上进行的,不需要额外的同步措施。这是因为在写操作期间,读操作仍然可以访问原始数组,不会受到写操作的影响。这样可以保证读操作的线程安全性。

  • 使用volatile关键字:CopyOnWriteArrayList内部使用了volatile关键字来保证多线程之间的可见性。当一个线程修改了列表时,它会将新的副本赋值给volatile修饰的数组引用,以便其他线程可以看到最新的修改。

  • 迭代器的一致性:CopyOnWriteArrayList的迭代器提供了弱一致性的保证。即,迭代器在创建时会获取一个快照,并在迭代过程中遍历该快照。这意味着迭代器不会反映出在迭代过程中对列表所做的修改。

CopyOnWriteArrayList实现了线程安全性。每个线程在修改操作时都会在自己的副本上进行操作,不会影响其他线程的读取操作。这样就避免了传统的同步机制(如锁)带来的竞争和阻塞,提供了高效的线程安全的List实现。

案例说明

假设我们有一个任务列表,多个线程同时对任务列表进行读取和修改操作。我们使用CopyOnWriteArrayList来存储任务列表,并保证线程安全。


public class TaskList {
    private CopyOnWriteArrayList<String> tasks = new CopyOnWriteArrayList<>();

    public void addTask(String task) {
        tasks.add(task);
    }

    public void removeTask(String task) {
        tasks.remove(task);
    }

    public void printTasks() {
        for (String task : tasks) {
            System.out.println(task);
        }
    }
}

我们创建多个线程来对任务列表进行读取和修改操作:

public class Main {
    public static void main(String[] args) {
        TaskList taskList = new TaskList();

        // 创建多个线程进行读取和修改操作
        Thread readerThread1 = new Thread(() -> {
            taskList.printTasks();
        });

        Thread readerThread2 = new Thread(() -> {
            taskList.printTasks();
        });

        Thread writerThread1 = new Thread(() -> {
            taskList.addTask("Task 1");
        });

        Thread writerThread2 = new Thread(() -> {
            taskList.removeTask("Task 1");
        });

        // 启动线程
        readerThread1.start();
        readerThread2.start();
        writerThread1.start();
        writerThread2.start();
    }
}

我们创建了两个读取线程(readerThread1和readerThread2)和两个修改线程(writerThread1和writerThread2)。读取线程通过调用printTasks方法来打印任务列表,修改线程通过调用addTask和removeTask方法来添加和删除任务。

由于CopyOnWriteArrayList的线程安全性,多个线程可以同时读取和修改任务列表,而不需要额外的同步措施。读取线程可以在修改线程进行操作的同时访问任务列表,并且不会受到修改操作的影响。

我们可以看到CopyOnWriteArrayList的线程安全性。每个线程在修改操作时都会在自己的副本上进行操作,不会影响其他线程的读取操作。这样就实现了多线程环境下的安全访问和修改任务列表。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Nuxt3】nuxt3目录文件详情描述:.nuxt、.output、assets、public、utils(一)

简言 nuxt3的中文网站 上次简单介绍了nuxt3创建项目的方法和目录文件大概用处。 这次详细说下.nuxt、.output、assets、public、utils五个文件夹的用处。 正文 .nuxt Nuxt在开发中使用.nuxt/目录来生成你的Vue应用程序。 为了避免将开发构建的输出推送到你的代码仓库中&…

使用HTTP/2在Linux上的Nginx服务器进行优化

随着互联网的发展&#xff0c;HTTP/2协议逐渐成为主流。与传统的HTTP/1.1相比&#xff0c;HTTP/2提供了更高的传输效率和更好的安全性。在Linux上使用Nginx服务器进行优化&#xff0c;我们可以充分利用HTTP/2的优势&#xff0c;提高网站的性能和用户体验。 1. 安装Nginx并启用…

【镜像制作】OS云主机镜像的制作——以H3C为例

一、云主机镜像简介 1&#xff0e;云主机镜像 云主机镜像不同于容器镜像&#xff0c;是一个含有引导分区、操作系统以及必要应用的单一文件&#xff0c;可以理解成是对整个系统安装光盘所有数据的克隆文件。云用户在创建和申请云主机时可通过选择不同的镜像来快速获取相应操作…

Linux/OpenAdmin

Enumeration nmap 用nmap扫描发现目标对外开放了22和80&#xff0c;端口详细信息如下 从nmap的结果看到&#xff0c;是apache的default page&#xff0c;使用工具跑一下目录&#xff0c;看了官 网文档的结果然后写个小字典节约时间&#xff0c;扫描结果如下 On the page at /…

【分布式技术】监控技术zabbix实操

目录 一、脚本监控nginx的连接状态 步骤一&#xff1a;做好nginx的配置 步骤二&#xff1a;完成监控数据脚本编写&#xff0c;并使用zabbix_get测试 步骤三&#xff1a;在zabbix agent配置目录中&#xff0c;编写以conf结尾的用户参数文件 步骤四&#xff1a;在zabbix web…

uniapp + node.js 开发问卷调查小程序

前后端效果图 后端&#xff1a;nodejs 12.8 ; mongoDB 4.0 前端&#xff1a;uniapp 开发工具&#xff1a;HBuilderX 3.99 前端首页代码 index.vue <!-- 源码下载地址 https://pan.baidu.com/s/1AVB71AjEX06wpc4wbcV_tQ?pwdl9zp --><template><view class&q…

探索设计模式的魅力:工厂方法模式

工厂方法模式是一种创建型设计模式&#xff0c;它提供了一种创建对象的接口&#xff0c;但将具体实例化对象的工作推迟到子类中完成。这样做的目的是创建对象时不用依赖于具体的类&#xff0c;而是依赖于抽象&#xff0c;这提高了系统的灵活性和可扩展性。 以下是工厂方法模式的…

在 Linux 本地部署 stable diffusion

由于工作站安装的是 ubuntu&#xff0c;卡也在上面&#xff0c;就只能在 ubuntu 上部署安装 stable diffusion 了。另外&#xff0c;Linux 上使用 stable diffusion 也会方便很多。 1 准备工作 NVIDIA 官网下载驱动&#xff0c;主要是为了规避多卡驱动不同的问题。由于本机是…

Linux下安装jdk、tomcat

linux下安装jdk、tomcat 一、linux下安装jdk1.1.下载Linux版本的JDK1.2.Linux安装JDk1.3.设置环境变量1.4.卸载JDK 二、linux下安装tomcat2.1.下载Linux版本的Tomcat2.2.在usr目录下新建tomcat目录2.3.进入到tomcat目录中解压下载的tomcat安装包2.4.配置环境变量-前提是已经安装…

C++ 设计模式之外观模式

【声明】本题目来源于卡码网&#xff08;题目页面 (kamacoder.com)&#xff09; 【提示&#xff1a;如果不想看文字介绍&#xff0c;可以直接跳转到C编码部分】 【简介】什么是外观模式 外观模式Facade Pattern , 也被称为“⻔⾯模式”&#xff0c;是⼀种结构型设计模式&#…

每日一练:LeeCode-102、二又树的层序遍历【二叉树】

本文是力扣LeeCode-102、二又树的层序遍历 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&…

visual studio的安装及scanf报错的解决

visual studio是一款很不错的c语言编译器 下载地址&#xff1a;官网 点击后跳转到以下界面 下滑后点击下载Vasual Sutdio&#xff0c;选择社区版即可 选择位置存放下载文件后&#xff0c;即可开始安装 安装时会稍微等一小会儿。然后会弹出这个窗口&#xff0c;我们选择安装位…

C++面试宝典第20题:计算岛屿数量

题目 在二维网格地图上,1 表示陆地,0 表示水域。如果相邻的陆地可以水平或垂直连接,则它们属于同一块岛屿。请进行编码,统计地图上的岛屿数量。比如:下面的二维网格地图,其岛屿数量为3。 解析 这道题主要考察应聘者对深度优先搜索、广度优先搜索、二维数组和矩阵操作、边…

Java代码审计FastJson反序列化利用链跟踪动态调试autoType绕过

目录 0x00 前言 0x01 基础参考 JNDI注入实例 使用type加入User类解析 FastJson历史漏洞简介 0x02 FastJson 1.2.24 利用链分析 调试过程 构造Poc思路 CC链关键流程 0x03 FastJson 1.2.25-1.2.47 利用链分析 1、开启autoTypeSupport&#xff1a;1.2.25-1.2.41 调试过…

#RAG##AIGC#检索增强生成 (RAG) 基本介绍和入门实操示例

本文包括RAG基本介绍和入门实操示例 RAG 基本介绍 通用语言模型可以进行微调以实现一些常见任务&#xff0c;例如情感分析和命名实体识别。这些任务通常不需要额外的背景知识。 对于更复杂和知识密集型的任务&#xff0c;可以构建基于语言模型的系统来访问外部知识源来完成任…

pl/sql程序块的使用

-- Created on 2024-01-15 by ADMINISTRATOR declare -- Local variables hererecord_tablename varchar2(100);---test_record表名record_StartNo integer(19);---test_record开始编号temp_No integer(19);maxnbbh integer(19);nCnt integer : 20;fi…

通用外设-W25Q64

前言 一、SPI通信 二、W25Q64基初时序 1.各种命令代码 2.代码 1.写使能指令 2.读取芯片是否忙碌状态并等待 3.写入数据 4.擦除函数操作 5.读取代码 三.验证 四.擦除说明 总结 前言 在单片机中一般32K FLASH就够用了&#xff0c;但是当我们使用图片或其他大量数据时…

K8s(二)Pod资源——node调度策略、node亲和性、污点与容忍度

目录 node调度策略nodeName和nodeSelector 指定nodeName 指定nodeSelector node亲和性 node节点亲和性 硬亲和性 软亲和性 污点与容忍度 本文主要介绍了在pod中&#xff0c;与node相关的调度策略&#xff0c;亲和性&#xff0c;污点与容忍度等的内容 node调度策略node…

深度学习中指定特定的GPU使用

目录 前言1. 问题所示2. 解决方法 前言 老生常谈&#xff0c;同样的问题&#xff0c;主要来源于&#xff1a;RuntimeError: CUDA error: out of memory 当使用完之后&#xff0c;想从其他方式调试&#xff0c;具体可看我这篇文章的&#xff1a;出现 CUDA out of memory 的解决…

Ps:认识路径

在 Photoshop 中&#xff0c;路径 Path广泛地应用于创建精确的图像边界&#xff08;包括精准抠图&#xff09;以及复杂的图形设计之中。 路径又称为“矢量路径”&#xff0c;或者“贝塞尔曲线” Bezier Curves路径。 路径本身只是一种基于数学方程的“轮廓指示”&#xff0c;并…