J.U.C Review - CopyOnWrite容器

news2024/11/13 14:57:12

文章目录

  • 什么是CopyOnWrite容器
  • CopyOnWriteArrayList
    • 优点
    • 缺点
    • 源码示例
  • 仿写:CopyOnWriteMap的实现
  • 注意事项

在这里插入图片描述

什么是CopyOnWrite容器

CopyOnWrite容器是一种实现了写时复制(Copy-On-Write,COW)机制的并发容器。在并发场景中,多个线程可能同时访问同一资源,当某个线程需要修改数据时,系统会创建该数据的副本供其修改,而其他线程仍然可以访问原始数据。这种机制的主要优点是可以在读操作频繁的情况下,避免加锁,从而提高读取性能。

在Java中,从JDK 1.5开始,提供了两个主要的CopyOnWrite容器:CopyOnWriteArrayListCopyOnWriteArraySet。这两个容器的设计使得在“读多写少”的场景下,能够有效地提高并发性能。


CopyOnWriteArrayList

优点

  1. 无需同步CopyOnWriteArrayList在进行读取操作时不需要任何同步措施,极大地提高了读取性能。

  2. 避免异常:在遍历时,即使有其他线程对列表进行修改,也不会抛出ConcurrentModificationException异常,因为读取和写入操作分别作用于不同的容器。

缺点

  1. 内存开销:每次写操作都会复制整个容器,导致内存使用增加,可能引发频繁的垃圾回收(GC)问题。

  2. 数据一致性问题:由于写操作和读操作作用于不同的容器,读操作可能读取到旧数据,因此不能保证实时一致性。

源码示例

CopyOnWriteArrayListaddremove方法的实现逻辑如下:

public boolean add(E e) {
   
    // ReentrantLock加锁,保证线程安全
    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();
    }
}

public E remove(int index) {
        
        // 加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = get(elements, index);
            int numMoved = len - index - 1;
            if (numMoved == 0)
                // 如果要删除的是列表末端数据,拷贝前len-1个数据到新副本上,再切换引用
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                // 否则,将要删除元素之外的其他元素拷贝到新副本中,并切换引用
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            // 解锁
            lock.unlock();
        }
    }

再来看看CopyOnWriteArrayList效率最高的读操作的源码

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

由上可见“读操作”是没有加锁,直接读取。


仿写:CopyOnWriteMap的实现

虽然Java并发包中没有提供CopyOnWriteMap,但可以参考CopyOnWriteArrayList实现一个简单的版本。以下是一个基本的实现示例:

import java.util.HashMap;
import java.util.Map;

public class CopyOnWriteMap<K, V> implements Map<K, V> {
    private volatile Map<K, V> internalMap = new HashMap<>();

    public V put(K key, V value) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<>(internalMap);
            V oldValue = newMap.put(key, value);
            internalMap = newMap;
            return oldValue;
        }
    }

    public V get(Object key) {
        return internalMap.get(key);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<>(internalMap);
            newMap.putAll(m);
            internalMap = newMap;
        }
    }
}

CopyOnWriteMap适用于“读多写少”的场景,例如一个搜索网站的黑名单管理系统。黑名单在特定时间更新,而在用户搜索时,系统需要快速检查关键字是否在黑名单中。


注意事项

使用CopyOnWrite容器时需要注意:

  1. 内存开销:应根据实际需求初始化容器的大小,以减少扩容带来的开销。

  2. 数据一致性CopyOnWrite容器只能保证最终一致性,不能保证实时一致性,因此不适用于需要立即读取写入数据的场景。

在这里插入图片描述

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

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

相关文章

2024年高教社杯数学建模国赛E题解题思路

E 题 交通流量管控 问题背景 随着城市化进程的加快、机动车的快速普及&#xff0c;以及人们活动范围的不断扩大&#xff0c;城市道路交通拥堵问题日渐严重&#xff0c;即使在一些非中心城市&#xff0c;道路交通拥堵问题也成为影响地方经济发展和百姓幸福感的一个“痛点”&a…

SpringDataJPA系列(6)Entiry注解使用

SpringDataJPA系列(6)Entiry注解使用 JPA协议规定 实体是直接进行数据库持久化操作的领域对象&#xff0c;必须通过 Entity 注解进行标示实体必须有一个 public 或者 protected 的无参数构造方法实体里面必须要有一个主键&#xff0c;主键标示的字段可以是单个字段&#xff0…

《机器学习》—— PCA降维

文章目录 一、PCA降维简单介绍二、python中实现PCA降维函数的介绍三、代码实现四、PCA降维的优缺点 一、PCA降维简单介绍 PCA&#xff08;主成分分析&#xff0c;Principal Component Analysis&#xff09;是一种常用的数据降维技术。它通过线性变换将原始数据转换到新的坐标系…

持久化分析

目录 介绍步骤WMI持久化分析注册表映像劫持IFEO持久化 介绍 1、WMI 的全称是 Windows Management Instrumentation&#xff0c;即 Windows 管理规范&#xff0c;在 Windows 操作系统中&#xff0c;随着 WMI 技术的引入并在之后随着时间的推移而过时&#xff0c;它作为一项功能…

Linux【6】系统

时间日期 date日期 cal——当月日历 cal -y 今年的日历 磁盘占用df du df 剩余空间 du 目录下的文件大小 进程ps ps aux a——其他用户 u——详细状态 x——没有控制终端 只看CPU占用高的进程top kill pid代号 ——杀死程序 通配符&#xff08;简略版&#xff09; …

每日OJ_牛客_解读密码(简单模拟)

目录 牛客_解读密码&#xff08;简单模拟&#xff09; 解析代码 牛客_解读密码&#xff08;简单模拟&#xff09; 解读密码__牛客网 解析代码 题目意思&#xff1a;给定字符串中包含其他符合一级数字&#xff0c;将字符串中数字解析出来。 解析步骤&#xff1a; 题目明确…

LabVIEW声发射数据采集系统开发

声发射&#xff08;Acoustic Emission, AE&#xff09;技术是材料检测中的一种无损检测方法&#xff0c;广泛用于结构健康监测。本文将介绍一个基于LabVIEW的声发射数据采集系统的真实案例&#xff0c;涵盖工作原理、开发流程、硬件选型、注意事项及难点。该系统通过LabVIEW平台…

LlamaIndex 使用 RouterOutputAgentWorkflow

LlamaIndex 中提供了一个 RouterOutputAgentWorkflow 功能&#xff0c;可以集成多个 QueryTool&#xff0c;根据用户的输入判断使用那个 QueryEngine&#xff0c;在做查询的时候&#xff0c;可以从不同的数据源进行查询&#xff0c;例如确定的数据从数据库查询&#xff0c;如果…

2024年装电脑,就认准这几个型号,能避坑!

前言 小伙伴是否都会觉得&#xff0c;自己又不懂电脑&#xff0c;跑电脑城去装机又怕被坑。这时候只能找熟人给装机&#xff0c;至少……熟人应该不会坑自己吧&#xff1f;&#xff01; 这不&#xff0c;小白电脑技术的抖音评论区上就有这么一条评论&#xff1a; 这哥们找一熟…

最新HTML5中的视频和音频讲解

第6章 HTML5中的视频和音频 H5新增video,audio,播放视频和音频&#xff0c;统称为多媒体元素。 6.1 多媒体元素基本属性 video用于电影文件和其他视频流的播放。 audio用于音乐文件和其他音频流的播放。 video的属性 src&#xff1a;文件路径&#xff0c;本地或者网络上。…

Android经典实战之SurfaceView原理和实践

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 SurfaceView 是一个非常强大但也相对复杂的 UI 组件&#xff0c;特别适用于对性能要求较高的绘制任务&#xff0c;如视频播放、游戏等。 1. Su…

Java 方法的定义

目录 1.Java的方法类似于其他语言的函数&#xff0c;是一段用来完成特定功能的代码片段。 2.方法包含一个方法头和方法体&#xff0c;下面是一个方法的所有部分&#xff1a; &#xff08;1&#xff09;修饰符&#xff1a;可选。告诉编译器如何调用该方法&#xff0c;定义了该…

Java笔试面试题AI答之JDBC(2)

文章目录 7. 列出Java应该遵循的JDBC最佳实践&#xff1f;8. Statement与PreparedStatement的区别,什么是SQL注入&#xff0c;如何防止SQL注入Statement与PreparedStatement的区别什么是SQL注入如何防止SQL注入 9. JDBC如何连接数据库&#xff1f;1. 加载JDBC驱动程序2. 建立数…

Python复杂网络社区检测:并行谱聚类算法设计与多种算法应用实战研究

原文链接&#xff1a;https://tecdat.cn/?p37574 分析师&#xff1a;Leiyun Liao 在当今的网络科学领域&#xff0c;复杂网络中的社区检测成为了一个至关重要的研究课题。随着信息技术的飞速发展&#xff0c;各种大规模网络不断涌现&#xff0c;如社交网络、生物网络等。准确地…

chapter12-异常(Exception)——(作业)——day15

目录 457-异常课后作业 458-异常课后作业2 457-异常课后作业 package chapter12.exception.homework;/*** author LuHan* version 1.0*/ public class Homework01 {public static void main(String[] args) {try {if(args.length!2){throw new ArrayIndexOutOfBoundsException…

立创商城9.9免邮活动开始啦!

从9月2日起&#xff0c;立创商城推出免邮活动&#xff0c;每月在领券中心>精选专区领取免邮券&#xff0c;即可享受满9.9元使用免邮券服务。 未注册的用户&#xff0c;可扫描下方二维码注册哦~

2024高教社杯数学建模国赛ABCDE题选题建议+初步分析

提示&#xff1a;DS C君认为的难度&#xff1a;C<B<A&#xff0c;开放度&#xff1a;A<C<B 。 D、E题推荐选E题&#xff0c;后续会直接更新E论文和思路&#xff0c;不在这里进行选题分析&#xff0c;以下为A、B、C题选题建议及初步分析 A题&#xff1a;“板凳龙”…

AI技术的新篇章:GPT Next、Gemini 2、GPT-6 和千代理人探索虚拟世界

在AI技术飞速发展的今天&#xff0c;许多令人兴奋的突破正逐渐进入公众视野。最近的新闻显示&#xff0c;诸如OpenAI的GPT Next、Google的Gemini 2.0、GPT-6以及模拟虚拟世界中的1000个AI代理人等前沿项目&#xff0c;标志着人工智能领域即将进入一个全新阶段。本文将深入探讨这…

多线程的简单了解——多客户端链接

在前面的学习中发现我们的聊天室功能只能有一个客户端接入服务端中&#xff0c;第二个客户端想要接入服务端中必须要等待第一个客户端输入结束才能接入。 这很明显不符合实际应用的开发&#xff0c;现在我们就来学习Java中一个重要的知识&#xff0c;多线程来解决这个问题。我们…

内存管理篇-22 高端内存和低端内存的分界线

这节课讲的主是为了区分低端内存和高端内存的是如何区分的&#xff1f;内核空间的划分是可以配置的。为了查看现象&#xff0c;通过qemu设置物理内存为不同情况。 结论&#xff1a;线性映射区的大小&#xff0c;和page_offset(内核起始地址0x80000000还是0xc0000000)和物理内存…