JUC高并发容器-CopyOnWriteArrayList

news2024/11/20 6:19:02

CopyOnWriteArrayList

  • JUC高并发容器
    • 线程安全的同步容器类
    • 什么是高并发容器?
    • CopyOnWriteArrayList

JUC高并发容器

线程安全的同步容器类

  Java同步容器类通过Synchronized(内置锁)来实现同步的容器,比如Vector、HashTable以及SynchronizedList等容器。线程安全的同步容器类主要有VectorStackHashTable等。另外,Java还提供了一组包装方法,将一个普通的基础容器包装成一个线程安全的同步容器。

  例如通过Collections.synchronized包装方法能将一个普通的SortedSet容器包装成一个线程安全的SortedSet

  1.通过synchronizedSortedSet静态方法包装出一个同步容器

public class CollectionsDemo {
    public static void main(String[] args) throws InterruptedException {
        //创建一个基础的有序集合
        SortedSet<String> elementSet=new TreeSet<>();
        //增加元素
        elementSet.add("element 1");
        elementSet.add("element 2");

        //将element包装成一个同步容器
        SortedSet<String> sortedSet = Collections.synchronizedSortedSet(elementSet);
        //输出容器中的元素
        System.out.println("SortedSet is :"+sortedSet);
        CountDownLatch latch = new CountDownLatch(5);
        ExecutorService pool = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 5; i++) {
            int finalI=i;
            pool.submit(()->{
                //向同步容器中增加一个元素
                sortedSet.add("element "+(3+finalI));
                System.out.println("add element"+(3+finalI));
                latch.countDown();
            });
        }
        latch.await();
        //输出容器中的元素
        System.out.println("SortedSet is :"+sortedSet);
    }
}

image-20230917175555706

  2.java.util.Collections所提供的同步包装方法

  java.util.Collections还提供了一系列对其他的基础容器进行同步包装的方法,如synchronizedList()方法将基础List包装成线程安全的列表容器,synchronizedMap()方法将基础Map容器包装成线程安全的容器,synchronizedCollection()方法将基础Collection容器包装成线程安全的Collection容器。

  同步包装方法如下:

image-20230917182029322

  与同步包装方法相对应,java.util.Collections还提供了一系列同步包装类,这些包装类都是其内部类。这些同步包装类的实现逻辑很简单:实现了容器的操作接口,在操作接口上使用synchronized进行线程同步,然后在synchronized的临界区将实际的操作委托给被包装的基础容器。

  3.同步容器面临的问题

  可以通过查看Vector、HashTable、java.util.Collections同步包装类的源码,发现这些同步容器实现线程安全的方式是:在需要同步访问的方法上添加synchronized关键字。

  synchronized在线程没有发生争用的场景下处于偏向锁的状态,其性能非常高。但是,一旦发生了线程争用,synchronized会由偏向锁膨胀成重量级锁,在抢占和释放时发生CPU内核态与用户态切换,所以削弱了并发性,降低了吞吐量,而且会严重影响性能。

  因此,为了解决同步容器的性能问题,有了JUC高并发容器。

什么是高并发容器?

  JUC高并发容器是基于非阻塞算法(或者无锁编程算法)实现的容器类,无锁编程算法主要通过CAS(Compare And Swap)+volatile组合实现,通过CAS保障操作的原子性,通过volatile保障变量内存的可见性。无锁编程算法的主要优点如下:

  • 开销较小:不需要在内核态和用户态之间切换进程。
  • 读写不互斥:只有写操作需要使用基于CAS机制的乐观锁,读读操作之间可以不用互斥。

1.List

  JUC包中的高并发List主要有CopyOnWriteArrayList,对应的基础容器为ArrayList

  CopyOnWriteArrayList相当于线程安全的ArrayList,它实现了List接口。在读多写少的场景中,其性能远远高于ArrayList的同步包装容器。

2.Set

  JUC包中的Set主要有CopyOnWriteArraySetConcurrentSkipListSet

  • CopyOnWriteArraySet继承自AbstractSet类,对应的基础容器为HashSet。其内部组合了一个CopyOnWriteArrayList对象,它的核心操作是基于CopyOnWriteArrayList实现的。
  • ConcurrentSkipListSet是线程安全的有序集合,对应的基础容器为TreeSet。它继承自AbstractSet,并实现了NavigableSet接口。ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的。

3.Map

  JUC包中Map主要有ConcurrentHashMapConcurrentSkipListMap

  • ConcurrentHashMap对应的基础容器为HashMap。JDK6中的ConcurrentHashMap采用一种更加细粒度的分段锁加锁机制,JDK8基于Synchronized+CAS实现。
  • ConcurrentSkipListMap对应的基础容器为TreeMap。其内部的Skip List(跳表)结构是一种可以代替平衡树的数据结构,默认是按照Key值升序的。

4.Queue

  JUC包中的Queue的实现类包括三类:单向队列、双向队列和阻塞队列。

  • ConcurrentLinkedQueue是基于列表实现的单向队列,按照FIFO(先入先出)原则对元素进行排序。新元素从队列尾部插入,而获取队列元素则需要从队列头部获取。
  • ConcurrentLinkedDeque是基于链表的双向队列,但是该队列不允许null元素。作为双向队列,ConcurrentLinkedDeque可以当作“栈”来使用,并且高效地支持并发环境。

  JUC还扩展了队列,增加了可阻塞地插入和获取等操作,提供了一组阻塞队列,具体如下:

  • ArrayBlockingQueue:基于数组实现的可阻塞地FIFO队列。
  • LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列。
  • PriorityBlockingQueue:按优先级排序的队列。
  • DelayQueue:按照元素的Delay时间进行排序的队列。
  • SynchronousQueue:无缓冲等待队列。

CopyOnWriteArrayList

   前面讲到,Collections可以将基础容器包装为线程安全的同步容器,但是这些同步容器包装类在进行元素迭代时并不能进行元素添加操作。

  (1)CopyOnWriteArrayList原理:

  CopyOnWrite(写时复制)就是在修改器对一块内存进行修改时,不直接在原有内存块上进行写操作,而是将内存复制一份,在新的内存中进行写操作,写完之后,再将原来的指针(或者引用)指向新的内存,原来的内存被回收。

  CopyOnWriteArrayList是写时复制思想的一种典型实现,其含有一个指向操作内存的内部指针array,而可变操作(add、set等)是在array数组的副本上进行的。当元素需要被修改或者增加时,并不直接在array指向的原有数组上操作,而是首先对array进行一次复制,将修改的内容写入复制的副本中。写完之后,再将内部指针array指向新的副本,这样就可以确保修改操作不会影响访问器的读取操作。原理如下图所示:

  通俗地说:读操作不会被写操作阻塞,读操作返回的结果可能不是最新的,适合读多写少的场景。

image-20230917173624498

  (2)CopyOnWriteArrayList读取操作:

  访问器的读取操作没有任何同步控制和锁操作,理由是内部数组array不会发生修改,只会被另一个array替换,因此可以保证数据安全。

//操作内存的引用 
private transient volatile Object[] array;
public E get(int index) {
        return get(getArray(), index);
}
//获取元素
private E get(Object[] a, int index) {
        return (E) a[index];
}
//返回操作内存
final Object[] getArray() {
        return array;
}

  (3)CopyOnWriteArrayList写入操作

  CopyOnWriteArrayList的写入操作add()方法在执行时加了独占锁以确保只能有一个线程进行写入操作,避免多线程写的时候会复制出多个副本。

  这块给出的都是部分源代码,API中重载方法很多

 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();	//释放锁
        }
 }
 final void setArray(Object[] a) {
        array = a;
 }

  从add()操作可以看出,在每次进行添加操作时,CopyOnWriteArrayList底层都是重新复制一份数组,再往新的数组中添加新元素,待添加完了,再将新的array引用指向新的数组。当add()操作完成后,array的引用就已经指向另一个存储空间了。

   既然每次添加元素的时候都会重新复制一份,那就增加了内存的开销,如果容器的写操作比较频繁,那么其开销就比较大。所以,在实际应用中,CopyOnWriteArrayList并不适合进行添加操作。但是在并发场景下,迭代操作比较频繁,CopyOnWriteArrayList就是一个不错的选择。

  (4)CopyOnWriteArrayList迭代器实现

  CopyOnWriteArrayList有自己的迭代器,该迭代器不会检查修改状态,也无需检查状态。因为被迭代的array数组可以说是只读的,不会有其他线程能够修改它。

 static final class COWIterator<E> implements ListIterator<E> {
        //数组的快照(snapshot)
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }
				//下一个元素
        public boolean hasNext() {
            return cursor < snapshot.length;
        }
 }

  迭代器的快照成员会在构造迭代器的时候使用CopyOnWriteArrayList的array成员去初始化,具体如下:

//获取迭代器 
public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
 }
//返回操作内存
 final Object[] getArray() {
     return array;
 }

  总结:

  1.CopyOnWriteArrayList的优点

  读取、遍历操作不需要同步,速度会非常快。所以CopyOnWriteArrayList适用于读操作多、写操作相对较少的场景(读多写少),比如可以在进行“黑名单”拦截时使用CopyOnWriteArrayList。

  2.CopyOnWriteArrayListReentrantReadWriteLock的比较

  CopyOnWriteArrayList和ReentrantReadWriteLock读写锁的思想非常类似,即读读共享、写写互斥、读写互斥、写读互斥。但是前者相比后者更进一步:为了将读取的性能发挥到极致,CopyOnWriteArrayList读取是完全不用加锁的,而且写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待,读操作的性能得到大幅提升

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

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

相关文章

react封装一个简单的upload组件(待完善)

目录 react封装一个简单的upload组件component / uploadImg / uploadImg.jsx使用效果 react封装一个简单的upload组件 component / uploadImg / uploadImg.jsx import React, { useState } from react; import { LoadingOutlined, PlusOutlined } from ant-design/icons; imp…

【源码】C/C++运动会计分系统 期末设计源码

文章目录 题目介绍功能源码效果展示带报告&#xff08;内容&#xff09; 题目介绍 使用语言&#xff1a; 两个版本都会发&#xff1a; 版本1&#xff1a;C语言 版本2&#xff1a; C 代码量&#xff1a; 500 题目介绍&#xff1a; 要求&#xff1a;初始化输入&#xff1a;N-参赛…

C++ 中的模型预测路径积分 (MPPI) 控制

一、说明 模型预测路径积分控制&#xff08;MPPI&#xff09;是一种基于采样的模型预测控制算法。是MPC控制模型的延申和拓宽&#xff0c;要了解MPPI需要先理解MPC&#xff0c;参见文章&#xff1a;MPC预测控制概述和C 中的模型库-CSDN博客 二、模型预测路径积分 (MPPI) 控制 模…

【小白专用23.10.22 已验证】windows 11 安装PHP8.2 +Apache2.4

环境说明 windows:windows 11 x64apache: Apache/2.4.43php :php-8.2.11 一.php 1、PHP下载 PHP For Windows: Binaries and sources Releases 注意&#xff1a; 1.要下载Thread Safe&#xff0c;否则没有php8apache2_4.dll这个文件&#xff1b;如果使用Apache作为服务器…

Casbin实战经验大揭秘:教育培训领域的创新实践和高效优化策略

策略描述语言&#xff08;论文&#xff09; 摘要: 为了保护云资源的安全&#xff0c;防止数据泄露和非授权访问&#xff0c;必须对云平台的资源访问实施访问控制。然而&#xff0c;目前主流云平台通常采用自己的安全策略语言和访问控制机制&#xff0c;从而造成两个问题&#x…

Jenkins简介及安装配置详解:开启持续集成之旅

目录 一、Jenkins介绍1.1 持续集成1.2 Jenkins持续集成的过程&#xff08;传统方式与自动化部署&#xff09; 二、环境搭建1、准备工作2、WEB发布服务器环境搭建3、SVN服务器环境搭建4、Jenkins环境搭建 一、Jenkins介绍 Jenkins是一个开源的、用Java编写的持续集成和持续交付&…

Defender Antivirus占用资源怎么禁止

前言 有时Defender Antivirus 突然磁盘IO很高。导致机器卡得很&#xff0c;开发代码很不方便&#xff0c;本文就介绍如何禁用这个服务。2f089809-2c6f-4fb7-86f5-8b5cbca8bd0d 操作 下载Defender Control https://www.sordum.org/9480/defender-control-v2-1/ 这是当前的最…

Adobe Photoshop 基本操作

PS快捷键 图层 选择图层 Ctrl T&#xff1a;可以对图层的大小和位置进行调整 填充图层 MAC: AltBackspace (前景) or CtrlBackspace (背景) WINDOWS: AltDelete (前景) or CtrlDelete (背景) 快速将图层填充为前景色或背景色 平面化图层&#xff08;盖印图层&#xff09…

uniapp实现登录组件之外区域置灰并引导登录

实现需求 每个页面需要根据用户是否登录决定是否显示登陆组件,登录组件半屏底部显示,登录组件之外区域置灰,功能按钮点击之后引导提示登录.页面效果如下: 实现思路说明 设置登录组件背景颜色为灰色,将页面分成登录区域(底部)和非登陆区域(上面灰色显示部分), 置灰区域添加…

嵌入式学习笔记(61)位操作符

2.1.1.位与& (1)注意&#xff1a;位与符号是一个&&#xff0c;两个&&是逻辑与。 (2)真值表&#xff1a;1&00 1&11 0&00 0&10 (3)从真值表可以看出&#xff1a;位与操作的特点是&#xff0c;只有1和1位于结果为1&#xff0c;其余全是0. (4)…

【Java】Java 17 新特性

Java 17 新特性概览 1. Java 17 简介2. Java 17 新特性类型推断 - 新的 var 关键字垃圾回收器改进JEP 356 增强的伪随机数生成器&#xff08;1&#xff09;提供了一个新接口 RandomGenerator&#xff08;2&#xff09;提供了一个新类 RandomGeneratorFactory&#xff08;3&…

Flutter的Don‘t use ‘BuildContext‘s across async gaps警告解决方法

文章目录 问题有问题的源码 问题原因问题分析Context的含义BuildContext的作用特殊情况 解决方法 问题 Flutter开发中遇到Don’t use BuildContext’s across async gaps警告 有问题的源码 if (await databaseHelper.isDataExist(task.title)) {showDialog(context: context,…

在PowerBI中提取IFC文件中的数据

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 在这篇文章中&#xff0c;我将逐步介绍从IFC文件中提取数据以创建 Shift IFC4PowerBI 函数的步骤。通过了解此工作流程&#xff0c;你能够更轻松地将 IFC 数据合并到自己的流程中。 在本文中&#xff0c;我们将重点关注文…

Reparameterization trick(重参数化技巧)

“Reparameterization trick”&#xff08;重参数化技巧&#xff09;是一种在训练生成模型中处理随机性潜在变量的方法&#xff0c;特别常见于变分自动编码器&#xff08;VAE&#xff09;等模型中。这个技巧的目的是使模型可微分&#xff08;differentiable&#xff09;&#x…

【源码】C++坦克大战源码

文章目录 题目介绍你收到的所有文件源码效果展示报告内容 题目介绍 代码量&#xff1a;1450 语言&#xff1a;C 你收到的所有文件 其中一个是devc版本&#xff0c;也可以用visual stdio 运行。 源码效果展示 typedef struct //这里的出现次序指的是一个AI_ta…

使用CountdownLatch和线程池批量处理http请求,并处理响应数据

背景和问题 ​ 背景&#xff1a;最近项目的一个接口数据&#xff0c;需要去请求其他多个服务器的数据&#xff0c;然后统一返回&#xff1b; 问题点&#xff1a;如果遍历所有的服务器地址&#xff0c;然后串行请求就会出现请求时间过长&#xff0c;加入需要请求十个服务器&…

Xray联动crawlergo自动扫描漏洞教程

xray和crawlergo联动 xray下载: https://github.com/chaitin/xray/releases crawlergo下载:360-crawlergo&#xff1a; github&#xff1a;https://github.com/Qianlitp/crawlergo/releases 联动脚本&#xff1a; github&#xff1a;https://github.com/timwhitez/crawlergo_x_…

Qt扫盲-QColor 理论使用总结

QColor 理论使用总结 一、概述1. 构造函数2. 合法性校验3. 组成值检索 二、整数与浮点精度三、Alpha-Blended Alpha混合图四、预定义颜色五、扩展的RGB颜色模型六、HSV颜色模型七、HSL颜色模型八、CMYK颜色模型 一、概述 QColor 是Qt里面描绘颜色的一个属性类&#xff0c;这个…

【算法练习Day25】 重新安排行程N 皇后 解数独

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 重新安排行程N 皇后解数独总…

图论05-【无权无向】-图的广度优先BFS遍历-路径问题/检测环/二分图/最短路径问题

文章目录 1. 代码仓库2. 单源路径2.1 思路2.2 主要代码 3. 所有点对路径3.1 思路3.2 主要代码 4. 联通分量5. 环检测5.1 思路5.2 主要代码 6. 二分图检测6.1 思路6.2 主要代码6.2.1 遍历每个联通分量6.2.2 判断相邻两点的颜色是否一致 7. 最短路径问题7.1 思路7.2 代码 1. 代码…