`

Java中的CopyOnWrite容器

阅读更多

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

什么是CopyOnWrite容器

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

 

CopyOnWriteArrayList的实现原理

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景,假如我们有一个搜索网站,用户在这个网站的搜索框中,输入关键字搜索内容,但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中,黑名单每天晚上更新一次。当用户搜索时,会检查当前关键字在不在黑名单当中,如果在,则提示不能搜索。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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);
    }
 
}

代码很简单,但是使用CopyOnWriteMap需要注意两件事情:

1. 减少扩容开销。根据实际需要,初始化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容器。

关于C++的STL中,曾经也有过Copy-On-Write的玩法,参见陈皓的《C++ STL String类中的Copy-On-Write》,后来,因为有很多线程安全上的事,就被去掉了。

 

 

from:http://coolshell.cn/articles/11175.html

分享到:
评论

相关推荐

    手工标注的包含两层类别结构的网页分类数据集_Hierarchical-WebSite-Theme-DataSet_SCU.zip

    手工标注的包含两层类别结构的网页分类数据集_Hierarchical-WebSite-Theme-DataSet_SCU

    基于粒子群算法的电力系统最优潮流 以IEEE30节点的六机为对象,建立考虑功率平衡、机组爬坡约束、出力限制约束的电力系统经济调

    基于粒子群算法的电力系统最优潮流 以IEEE30节点的六机为对象,建立考虑功率平衡、机组爬坡约束、出力限制约束的电力系统经济调度模型,采用粒子群算法对模型进行求解,得到六个机组的最优运行计划,确定系统最优运行成本。 这段程序主要是一个基于粒子群优化算法(PSO)的电力系统调度程序。它用于优化电力系统中火电、风电和光伏发电机组的出力,以实现最小化发电成本和最小化失负荷量的目标。 该程序的主要功能是根据给定的负荷数据、初始机组出力和风光发电数据,通过PSO算法求解最优的机组出力方案。它涉及到的领域是电力系统调度和优化。 程序的主要思路如下: 1. 首先,定义了一些参数,如最大迭代次数、搜索空间维数、粒子个数等。 2. 然后,加载了电力系统的一些数据,包括机组的发电成本、负荷数据、风电数据和光伏数据。 3. 接下来,使用PSO算法对每个小时的机组出力进行优化,得到最优的机组出力方案。 4. 计算每个小时的发电成本、失负荷量、弃风弃光量等指标。 5. 绘制机组出力曲线、风电出力曲线、光伏出力曲线、负荷曲线和成本变化曲线。 程序中还包含一个名为"pso"的子函数,用于实现PSO算法的主要

    基于微信小程序的四六级词汇的开题报告.docx

    基于微信小程序的四六级词汇的开题报告.docx

    【超强组合】基于matlab黑翅鸢算法BKA-BP-Adaboost数据分类预测【含Matlab源码 8146期】.zip

    CSDN海神之光上传的全部代码均可运行,亲测可用,直接替换数据即可,适合小白; 1、代码压缩包内容 主函数:Main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2023b;若运行有误,根据提示修改;若不会,可私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开除Main.m的其他m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描博主博客文章底部QQ名片; 4.1 CSDN博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作 智能优化算法优化-BP-Adaboost数据分类预测系列程序定制或科研合作方向: 4.4.1 遗传算法GA/蚁群算法ACO优化-BP-Adaboost数据分类预测 4.4.2 粒子群算法PSO/蛙跳算法SFLA优化-BP-Adaboost数据分类预测 4.4.3 灰狼算法GWO/狼群算法WPA优化-BP-Adaboost数据分类预测 4.4.4 鲸鱼算法WOA/麻雀算法SSA优化-BP-Adaboost数据分类预测 4.4.5 萤火虫算法FA/差分算法DE优化-BP-Adaboost数据分类预测 4.4.6 其他优化算法优化-BP-Adaboost数据分类预测

    批处理修复桌面异常图标

    在Windows 操作系统中,用户可能会遇到各种与桌面图标相关的问题,如图标重叠、图标不显示或者图标之间的间距异常。"Win桌面图标修复工具"正是为了解决这些困扰用户的问题而设计的。这个工具能够有效地帮助用户修复这些问题,使Windows 的桌面环境恢复正常,提供更加整洁、有序的视觉体验。 1. **图标重叠问题**:在某些情况下,由于系统错误或第三方应用的冲突,Windows 11桌面的图标可能出现重叠现象,导致用户难以正常访问或区分各个图标。Win11桌面图标修复工具通过重新排列和定位桌面图标,自动解决这种重叠问题,让用户能够清晰地看到每个图标的完整内容。 2. **图标不显示**:有时候,部分桌面图标可能因系统更新、驱动程序冲突或文件损坏而不显示。该工具能检测并修复可能导致图标不显示的潜在问题,如缓存文件损坏或注册表错误,恢复图标显示,确保用户可以正常使用所有应用程序。 3. **图标间距过大或过小**:在Windows 中,用户可能发现图标之间的间距突然变得过大或过小,这可能是系统设置出现问题或被意外更改。修复工具会调整桌面图标的间距,使其恢复到默认状态。

    【SCI2区】白鲸算法BWO-BiTCN-BiGRU-Attention风电预测【含Matlab源码 8071期】.zip

    CSDN海神之光上传的全部代码均可运行,亲测可用,直接替换数据即可,适合小白; 1、代码压缩包内容 主函数:Main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2023b;若运行有误,根据提示修改;若不会,可私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开除Main.m的其他m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描博主博客文章底部QQ名片; 4.1 CSDN博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作 智能优化算法优化-BiTCN-BiGRU-Attention风电预测系列程序定制或科研合作方向: 4.4.1 遗传算法GA/蚁群算法ACO优化-BiTCN-BiGRU-Attention风电预测 4.4.2 粒子群算法PSO/蛙跳算法SFLA优化-BiTCN-BiGRU-Attention风电预测 4.4.3 灰狼算法GWO/狼群算法WPA优化-BiTCN-BiGRU-Attention风电预测 4.4.4 鲸鱼算法WOA/麻雀算法SSA优化-BiTCN-BiGRU-Attention风电预测 4.4.5 萤火虫算法FA/差分算法DE优化-BiTCN-BiGRU-Attention风电预测 4.4.6 其他优化算法优化-BiTCN-BiGRU-Attention风电预测

    _设计了同ResNet18,ResNet50以及ResNet101,相似参数量的VisionTran

    _设计了同ResNet18,ResNet50以及ResNet101,相似参数量的VisionTran_VisionTransformer-vs-ResNet-CIFAR100

    图像去雾,MATLAB方法。带界面,实现不同方法选择,显示去雾前后效果图。_shipinquwu.zip

    图像去雾,MATLAB方法。带界面,实现不同方法选择,显示去雾前后效果图。_shipinquwu

    实现曲线的分段拟合,拟合方法与matlab的polyfit相同_DataFitting.zip

    实现曲线的分段拟合,拟合方法与matlab的polyfit相同_DataFitting

    基于树莓派、OpenCV及Python语言的人脸识别毕业设计

    【作品名称】:基于树莓派、OpenCV及Python语言的人脸识别【毕业设计】 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】: 使用OpenCV for Python图像识别库,运行在树莓派RASPBIAN JESSIE Linux系统平台上,搭配树莓派官方摄像头模块。 运行要求 OpenCV 2.4.9 for Python Python 2.7 v4l2 PyQt4 注意 该示例运行的屏幕分辨率为竖屏480 x 800,可以修改 /boot/config.txt 的以下配置 安装PyQt4 sudo apt-get install python-qt4 运行 python main.py 【资源声明】:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。需要有一定的基础能够看懂代码,能够自行调试代码并解决报错,能够自行添加功能修改代码。

    数值计算(Matlab)第四版部分课后题代码实现(python)_Numerical_Analysis_Project.zip

    数值计算(Matlab)第四版部分课后题代码实现(python)_Numerical_Analysis_Project

    手写KNN对mnist数据集进行分类_KNN.zip

    手写KNN对mnist数据集进行分类_KNN

    大学生创业可行报告行业园区建筑房产可行性报告

    大学生创业可行报告行业园区建筑房产可行性报告

    Python中实现的多尺度协同变异粒子群优化算法.zip

    Python中实现的多尺度协同变异粒子群优化算法

    慈利县第一中学_物化生原始_学校等2个文件.zip

    慈利县第一中学_物化生原始_学校等2个文件.zip

    项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻。是对在线教育平台业务进行_LeadNews.zip

    项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻。是对在线教育平台业务进行_LeadNews

    社区网格化管理平台 源码+数据库+论文(JAVA+SpringBoot+Vue.JS+MySQL).zip

    社区网格化管理平台 源码+数据库+论文(JAVA+SpringBoot+Vue.JS+MySQL) 启动教程:https://www.bilibili.com/video/BV11ktveuE2d

    这是一个基于Linux平台,C++编写的分布式系统。服务器端采用了线程池+

    这是一个基于Linux平台,C++编写的分布式系统。服务器端采用了线程池+Libevent的IO模型_distributed-system-framework

    大学生创业可行报告行业路桥可行性报告大学生创业可行报告行业路桥可行性报告

    大学生创业可行报告行业路桥可行性报告大学生创业可行报告行业路桥可行性报告

    基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip

    基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip 【资源说明】 适用人群:该项目属于高分优质项目,上传之前都本地运行验证过。适合小白、高校学生、教师、科研人员、公司员工下载学习借鉴使用。 用途:学习借鉴,也可在此基础上二次开发,当然也可以直接用于课设、作业、毕设、实际项目等。 技术支持:关于项目的技术细节或更详细的介绍,可以私信与我沟通,或看项目内的项目说明(若有)、代码等,很乐意交流学习。 【特别强调】 若自己基础实在太差,自己不懂运行,可以与我私聊,可远程教学指导。当然也可以做项目二次开发和定制。 基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip基于ffmpeg和ffmpeg.js的web版视频编辑器项目源码+项目说明.zip

Global site tag (gtag.js) - Google Analytics