什么是copyonwrite容器

2023-11-05

开发十年,就只剩下这套Java开发体系了 >>>   hot3.png

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

    

CopyOnWriteArrayList的实现原理

在使用CopyOnWriteArrayList之前,我们先阅读其源码了解下它是如何实现的。以下代码是向ArrayList里添加元素,可以发现在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来。

public boolean add(T 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;
}

 

读的时候不需要加锁,如果读的时候有多个线程正在向ArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的ArrayList。

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

JDK中并没有提供CopyOnWriteMap,我们可以参考CopyOnWriteArrayList来实现一个,基本代码如下:

import java.util.Collection;
import java.util.Map;
import java.util.Set;
 
public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
    private volatile Map<K, V> internalMap;
 
    public CopyOnWriteMap() {
        internalMap = new HashMap<K, V>();
    }
 
    public V put(K key, V value) {
 
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            V val = newMap.put(key, value);
            internalMap = newMap;
            return val;
        }
    }
 
    public V get(Object key) {
        return internalMap.get(key);
    }
 
    public void putAll(Map<? extends K, ? extends V> newData) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            newMap.putAll(newData);
            internalMap = newMap;
        }
    }
}

实现很简单,只要了解了CopyOnWrite机制,我们可以实现各种CopyOnWrite容器,并且在不同的应用场景中使用。

CopyOnWrite的应用场景

CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景,假如我们有一个搜索网站,用户在这个网站的搜索框中,输入关键字搜索内容,但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中,黑名单每天晚上更新一次。当用户搜索时,会检查当前关键字在不在黑名单当中,如果在,则提示不能搜索。实现代码如下:

package com.ifeve.book;
 
import java.util.Map;
 
import com.ifeve.book.forkjoin.CopyOnWriteMap;
 
/**
 * 黑名单服务
 *
 * @author fangtengfei
 *
 */
public class BlackListServiceImpl {
 
    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(
            1000);
 
    public static boolean isBlackList(String id) {
        return blackListMap.get(id) == null ? false : true;
    }
 
    public static void addBlackList(String id) {
        blackListMap.put(id, Boolean.TRUE);
    }
 
    /**
     * 批量添加黑名单
     *
     * @param ids
     */
    public static void addBlackList(Map<String,Boolean> ids) {
        blackListMap.putAll(ids);
    }
 
}


1. 减少扩容开销。根据实际需要,初始化CopyOnWriteMap的大小,避免写时CopyOnWriteMap扩容的开销。代码很简单,但是使用CopyOnWriteMap需要注意两件事情:

2. 使用批量添加。因为每次添加,容器每次都会进行复制,所以减少添加次数,可以减少容器的复制次数。如使用上面代码里的addBlackList方法。

CopyOnWrite的缺点

CopyOnWrite容器有很多优点,但是同时也存在两个问题,即内存占用问题和数据一致性问题。所以在开发的时候需要注意一下。

内存占用问题。因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存)。如果这些对象占用的内存比较大,比如说200M左右,那么再写入100M数据进去,内存就会占用300M,那么这个时候很有可能造成频繁的Yong GC和Full GC。之前我们系统中使用了一个服务由于每晚使用CopyOnWrite机制更新大对象,造成了每晚15秒的Full GC,应用响应时间也随之变长。

针对内存占用问题,可以通过压缩容器中的元素的方法来减少大对象的内存消耗,比如,如果元素全是10进制的数字,可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器,而使用其他的并发容器,如ConcurrentHashMap

数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是copyonwrite容器 的相关文章

  • Spring Security:如何在 FilterRegistrationBean 中使用多个 URL 模式?

    我有一颗豆子 Bean public FilterRegistrationBean animalsFilterRegistration FilterRegistrationBean registration new FilterRegist
  • JSON - 使用Gson反序列化动态对象

    假设我有一个以下类型的 Java 类 public class MyClass public String par1 public Object par2 然后我有这个 String json par1 val1 par2 subpar1
  • 哪个 new 首先执行——在构造函数中还是在构造函数外?

    如果我定义一个类如下 public class myClass private x new anotherClass private y public myClass y new anotherClass 哪个变量会更早获得实例 x 或 y
  • 在 Java 中使用 Apache POI XWPF 在同一个 Word 文档中横向和纵向页面

    我正在尝试使用 Java 和 Apache POI 库创建一个包含一些横向页面和一些纵向页面的 Word 文档 我可以更改所有页面的方向 但有没有办法只更改其中某些页面的方向 我尝试过使用不同的部分和主体 但无济于事 目前我已经编写了一个函
  • 使用 Java NIO 直接访问 Windows 磁盘

    我正在使用一个使用 Java NIO 的库来直接将文件映射到内存 但我在直接读取磁盘时遇到问题 I can直接使用读取磁盘FileInputStream与 UNC 合作 例如 File disk new File PhysicalDrive
  • 通过 JDBC 调用 Sybase 存储过程时结果集为空

    我正在调用一个通过 JDBC 返回多个结果集的 Sybase 存储过程 我需要获取一个特定的结果集 其中有一列名为 结果 这是我的代码 CallableStatement cs conn prepareCall sqlCall cs reg
  • 在 Java 和 C 中在运行时调用名为“string”的方法

    我们如何调用名称为的方法string在运行时 谁能告诉我如何在 Java 和 C 中做到这一点 在java中可以通过反射api来完成 看一下Class getMethod String methodName Class parameterT
  • 将图像缩略图上传到服务器,而不上传整个图像

    据我所知 我在这里问的是不可能的 但我想无论如何我都会问 以防我遗漏了什么 假设您想让用户上传 JPG 图像 并且这些图像被缩放为较小的图标 并且原始图像始终被丢弃并且不再需要 有没有什么方法可以在大多数现代浏览器中普遍使用 让用户选择硬盘
  • 在 Eclipse 中导航 Java 调用堆栈

    在调试器中像GDB http sources redhat com gdb 当您在断点处停止时 您可以轻松地向上移动调用堆栈并检查相关的源和堆栈帧数据 在 Eclipse 中如何做到这一点 In the 调试视角 http www ibm
  • 如何在Android Studio中关联.mp3文件

    我想根据列表视图项单击播放 mp3 文件 但是根据我的代码 我运行我的应用程序 出现此窗口 因此由于缺少音频选项 我真的不知道需要选择其中哪一个为了关联我的 mp3 文件 mainList setOnItemClickListener ne
  • 用 Java 捕获扬声器输出

    使用Java可以捕获扬声器输出吗 此输出不是由我的程序生成的 而是由其他正在运行的应用程序生成的 这可以用 Java 完成还是我需要求助于 C C 我有一个基于 Java 的应用程序 使用过的爪哇声音 https stackoverflow
  • 如何从 REstAssured 中的 Json 数组获取 JSON 对象

    任何人都可以帮我解决这个场景 我是新来的RestAssured和处理JSON在我们的自动化脚本中 我有一个API谁的回应是JSONArray i e id 1002 entity testcase fieldName TextName di
  • 将 Class 对象转换为字节

    如果我有一个Class http java sun com j2se 1 5 0 docs api java lang Class html在运行时实例 我可以获得它的 byte 表示形式吗 我感兴趣的字节将在类文件格式 http java
  • 使用JPanel绘制直线并获取点坐标

    我现在完全不知所措 我没有太多用 Java 构建 GUI 我一直在阅读有关 swing 和 JPanel 的所有内容 我认为我想做的事情是可能的 我只是还没有弄清楚how 我正在尝试构建一个 GUI 您可以在其中在某个绘图区域内绘制直线 我
  • Spring @Configuration如何缓存对bean的引用

    使用基于 Java 的配置时 Spring 如何防止再次调用 bar 我想知道编译时注释处理或通过代理方法 Configuration public class AppConfig Bean public Foo foo return ne
  • Java 通用问题

    下面的代码可以编译 但如果我取消注释行 它不会编译 我很困惑为什么 HashMap 确实扩展了 AbstractMap 并且声明映射的第一行可以正常编译 import java util AbstractMap import java ut
  • Hive NVL 不适用于列的日期类型 - NullpointerException

    我正在使用 HDFS 上的 MapR Hive 发行版并面临以下问题 如果表的列类型是 日期 类型 则NVL https cwiki apache org confluence display Hive LanguageManual UDF
  • 我可以从同一个 jar 文件执行两个不同的类吗?

    我有一个项目 在一个包中我制作了服务器 在第二个包中我制作了客户端 它运行良好 我想创建一个 Jar 文件 是否可以使用同一个 jar 文件分别运行客户端和服务器 我使用了只有一个 main 的 jar 文件 当我运行 jar 文件时 它会
  • Selenium Webdriver 中的 IF 语句

    我想知道是否有人可以帮助我解决我正在尝试解决的问题以及 Java 中 Webdriver 的 If 语句 当登录到我正在测试的应用程序时 可以在主页之前进入安全问题页面 如果是新用户等 我希望测试中的代码做的是 如果出现安全问题页面 请填写
  • 确保对象实现 Comparable

    我有一个小问题 想知道如何解决它 我有一个通用类Tuple

随机推荐

  • 红队靶场搭建

    这篇博客主要是讲解一下vulstack红队评估的靶场 从信息收集到控制DMZ区域的WEB服务器 接着联合MSF和CS控制内网机器 随后通过搭建隧道 配置代理 攻击内网机器并且控制域控 主要讲解红队的攻击的全部流程 首先主要讲解一下靶场环境的
  • 几个汇编代码

    文章目录 输入输出 输出2 16进制 冒泡排序 判断正数负数零 以有符号数输出 输入输出 DATAS SEGMENT arr dw 20 dup num db 0 string1 db input the number of interge
  • 【Debug】关于Could not get lock /var/lib/dpkg/lock-frontend解决办法

    作者 柒号华仔 个人主页 欢迎访问我的主页 个人信条 星光不问赶路人 岁月不负有心人 个人方向 专注于5G领域 同时兼顾其他网络协议 编解码协议 C C linux等 感兴趣的小伙伴可以关注我 一起交流 目录 现象 原因 解决办法 现象 在
  • 前端js通过ajax请求下载Blob类型文件(XMLHttpRequest)

    方式一 必须通过原生ajax实现 jquery封装的ajax会有问题 function exportDevice params let xhr new XMLHttpRequest xhr open post http localhost
  • LORA项目源码解读

    大模型fineturn技术中类似于核武器的LORA 简单而又高效 其理论基础为 在将通用大模型迁移到具体专业领域时 仅需要对其高维参数的低秩子空间进行更新 基于该朴素的逻辑 LORA降低大模型的fineturn门槛 模型训练时不需要保存原始
  • 地球坐标,火星坐标,百度坐标转换

    标准地球坐标即GPS设备获得的坐标 该坐标需要经过国家测绘局进行加密后形成火星坐标 WGS 84 我们用的google坐标和高德地图坐标也就是火星坐标 百度地图 在火星坐标的基础上再进行一次加密 形成了百度地图上的坐标 因此 直接将标准地球
  • 电脑出现您的计算机配置似乎正确的,Win10提示你的计算机配置似乎是正确的怎么解决?...

    电脑使用久了总是会出现一些问题 最常见的就是系统提示了 相信不少用户都遇到过win10提示你的计算机配置似乎是正确的 但是又不知道怎么办 为此小编赶紧整理了以下教程帮助大家 大家赶紧来看看吧 访问 解决方法 1 首先进入桌面 按下 win
  • echarts自定义仪表盘实现特殊功能:轴线渐变(半透明) + 刻度颜色变化 + 仪表盘轴线宽度自适应

    如图是我画的两个仪表盘 第一个仪表盘 实现了 轴线渐变 半透明 刻度颜色变化 仪表盘轴线宽度自适应 第二个仪表盘 实现了 仪表盘轴线宽度自适应 关于轴线的渐变 本来打算使用img 但是由于此轴线是透明度渐变 不同位置透明度不一样 所以无法使
  • vue定时器无法停止。

    vue模板里面的定时器问题 文章目录 前言 一 定时器是什么 二 使用步骤 1 使用过程 2 读入数据 总结 前言 有些时候我们在使用vue模板时 不免会使用定时器来定时请求后台获取数据 但是在获取数据的时候 定时器就像踩不住刹车的小车 飘
  • Nuxt.js实现SSR的应用

    seo 即 搜索引擎优化 Search Engine Optimization 它是指通过站内优化 如 网站结构调整 网站内容建设 网站代码优化以及站外优化等方法 来进行搜索引擎优化 简单说 通过各种技术 手段 来确保 你的Web内容被搜素
  • 股海心法60条

    看不懂 看不准 没把握时坚决不进场 先学会做空 再学会做多 君子问凶不问吉 高手看盘先看跌 贪婪与恐惧 投资之大忌 侥幸是加大风险的罪魁 犹豫则是错失良机的祸首 心态第一 策略第二 技术只有屈居第三 任何时候不要轻易满仓 这样做 有利于保持
  • MySQL的多表关联查询

    一 多表关联查询 多表关联查询是使用一条SQL语句 将关联的多张表的数据查询出来 1 1 交叉查询 交叉查询就是将多张表的数据没有条件地连接在一起进行展示 1 1 1 语法 使用交叉查询类别和商品 目标 查询所有分类 以及每个分类下的所有商
  • 企业微信开发(通讯录模块)

    1 前言 关于企业微信通讯录同步的开发 先获取企业corpid 通讯录应用的secret 并在通讯录应用开启API编辑通讯录权限 2 关于token token是通过调用微信api接口由corpid和secret获取 由于token是每隔两
  • 系统提示0x00000709错误怎么办?

    一般都是系统自动更新补丁造成的 系统更新出现大面积打印机造成电脑蓝屏的问题还没解决多久 又出现了更新系统造成打印机无法共享 现在的系统都不带测试就推送的吗 所以我一般强烈建议大家不要更新系统 更新系统就是个定时炸弹 随时可能让你的系统爆炸
  • 链式栈的创建以及各种操作

    目录 一 声明 二 链式栈的初始化和各种操作 2 1 链栈结构定义 2 2 初始化和进栈操作 2 3 进栈结果展示 编辑 2 4 出栈操作 2 5 出栈结果展示 2 6 遍历操作 2 7 遍历结果展示 编辑 2 8 获取栈顶元素 2 9 结
  • JS对象

    前言 回顾之前的七种数据类型 number string bool symbol null undefined object 五个falsy值 null undefined 0 NaN 空字符串 对象的概念 定义 无序的数据集合 键值对的集
  • openstack之neutron Local网络

    openstack之neutron Local网络 一 local网络介绍 二 修改neutron的相关配置文件 1 进入配置文件目录 2 备份配置文件 3 修改配置文件 三 重启服务 四 管理员创建local网络 1 创建网络 2 创建子
  • 机器学习西瓜书吃瓜笔记之(一)深入理解线性模型与logistics回归

    入门概念 机器学习两大基本问题 预期的输出是离散还是连续 回归问题 用多个变量拟合出一个连续值 分类问题 用多个变量拟合出一个离散值 机器学习三大理论 确定研究手段 传统监督学习 血糖预测 有无糖尿病预测 深度学习 自然语言处理 计算机视觉
  • hosts文件的作用以及hosts中多个ip映射一个域名地址的解析顺序

    hosts的作用 当我们访问网站时 要首先通过DNS服务器把网络域名 www xx com 解析成IP地址 我们的计算机才能访问 如果对于每个域名请求我们都要等待域名服务器解析后返回IP信息 这样访问网络的效率就会降低 而Hosts文件就能
  • 什么是copyonwrite容器

    开发十年 就只剩下这套Java开发体系了 gt gt gt CopyOnWrite容器即写时复制的容器 通俗的理解是当往一个容器添加元素的时候 不直接往当前容器添加 而是先将当前容器进行Copy 复制出一个新的容器 然后新的容器里添加元素