Java中的CopyOnWrite
2019独角兽企业重金招聘Python工程师标准>>>
什么是CopyOnWrite
CopyOnWrite(COW),写时复制。 其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。 通俗的理解是当我们往一个CopyOnWrite容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
<!-- more -->
CopyOnWrite在Java中的应用
通过搜索我们可以发现有三个结果(JDK1.8),分别是
- CopyOnWriteArrayList
- CopyOnWriteArraySet
- CopyOnWriteMap
其中CopyOnWriteMap时Sun公司自己的实现,并不属于JDK; 我们就通过分析一下CopyOnWriteArrayList的源码看看和ArrayList的区别。
首先通过构造方法,我们就会发现和平时的容器构造方法好像有点不一样:没有生成指定容器大小的构造方法。 为什么要这么做?如果不能生成指定指定大小的容器,add()方法肯定会受到影响。
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();}}
果然,add()方法和ArrayList的add()方法不一样:
- 多了一把锁
- 每次添加都要copy一次
为什么要加锁? CopyOnWrite容器一般应用在多线程的条件下。如果不加锁,有五个线程同时添加元素,那么内存中就会用五份拷贝。这样不仅不能保证多线程下的安全性也浪费了大量的内存空间。
为什么每次添加元素都要copy呢? 要解释这个问题,我们得要它的构造方法说起。通过前面的分析,我们知道它的构造方法是不能构造指定大小的容器。来看一下默认构造方法:
public CopyOnWriteArrayList() {setArray(new Object[0]);}
只是生成了一个大小为0的数组!结合另外两个构造方法可以发现,所有的构造方法保证数组中的每个元素都不为null。我推断出这样做的原因为了:
- 不浪费内存
- 保证多线程下的安全性
因为无论是添加、删除还是迭代操作,为了能够在多线程下保证安全性都是先对快照操作然后再替换掉原来的数组。如此以来,数组中空余的部分变得毫无用处而且还浪费了宝贵的内存空间!
CopyOnWrite的不足
CopyOnWrite有很多好处,例如在读多写少且对数据的一致性要求不是很高的时候可以考虑采用CopyOnWrite容器,应用场景如:网站黑名单,商品类目的存储,但是我们也要注意到它不足的地方:
内存占用问题 假如元素组有一百兆的数据,add一百兆的数据,这时候内存中就会存在三百兆数据。删除操作也存着类似问题
数据的一致性 因为添加、删除都是先对快照进行操作,所以就有可能出现数据不一致的情况,编程的时候要注意到这一点!
性能问题 添加操作会调用Arrays.copyOf(),拷贝整个数组;而删除操作更耗时,将数组中的元素一个一个拷贝。 解决办法是尽量批量操作,避免单个操作。
通过分析,其实CopyOnWrite并不是想象中的那么高深,理解它的思想分析起来就轻松啦~
参考
聊聊并发-Java中的Copy-On-Write容器
转载于:https://my.oschina.net/liuxiaomian/blog/748161
Java中的CopyOnWrite相关推荐
- 聊聊并发-Java中的Copy-On-Write容器
Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...
- java中的数据结构总结
Java的类库实在是很多,以至于很多人都不太了解,结果总是自己造轮子. 下面汇总了Java中的一些数据结构,加上一些实现的分析,同时备忘. 至于时间复杂度,个人觉得写出来的用处不大.如果明白它是怎么实 ...
- Java中使用ArrayList的10个示例–教程
Java中的ArrayList是HashMap之后最常用的集合类. Java ArrayList表示一个可自动调整大小的数组,并用于代替数组. 由于创建数组后我们无法修改数组的大小,因此我们更喜欢在J ...
- 零拷贝技术在 Java 中为何这么牛?
[CSDN 编者按]大家在学计算机的过程中是否会遇到"零拷贝"这样的字眼,本文针对这一技术为大家提供详细的概念解释,并对其产出的意义及其在Java中的应用进行说明. 责编 | 欧阳 ...
- Java中的设计者模式
创建型模式 1.单例模式 概念:单例模式,是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中,应用该模式的类只有一个实例.即一个类只有一个对象实例. 实 ...
- java基础--java中HashMap原理
java中HashMap原理 内推军P21 P22 1.为什么用HashMap? HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射HashMap采用了数组和链表 ...
- java中怎么给方法加锁_Java中,我会用ArrayList,怎么还要会用CopyOnWriteArrayList
前言 之前的文章已经说过了java开发中,保存集合数据是用ArrayList还是LinkedList,了解下:Java编程中我该用ArrayList还是LinkedList? 平常面试过程中问的最多的 ...
- Java中的设计模式:“代理模式”的理解
代理模式定义: 为其他对象提供一种代理以控制对这个对象的访问.在面向对象中,有时候直接访问一些对象比较麻烦,所以代理模式就是在这个对象上加上一个访问该对象的访问层.类似于很多明星的事务实际都是交给经纪 ...
- java中的锁(一)(锁的介绍)
转载:https://blog.csdn.net/zqz_zqz/article/details/70233767/ 测试结果: 1. 单线程下synchronized效率最高(当时感觉它的效率应该是 ...
最新文章
- Messages 贪心,期望,概率,模拟(2000)
- 如何通过dblink truncate远程数据库上的表
- java 数字图片识别_java – 识别图像中的数字
- l360废墨收集垫清零_知识分享003:EPSON L360打印机出现故障-废墨计数清零
- C++异常处理类与自定义异常处理类
- python接口自动化(四十)- logger 日志 - 下(超详解)
- 黄刘生--数据结构--答案 2
- Linux system函数返回值
- 快应用实现网络测速功能_网络阅卷系统应用系统功能实现情况
- 安装Git SCM for Windows
- 华为虚拟机eNSP命令大全
- 基于java jsp企业人事管理系统mysql
- 已解决:[emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forb
- 很重要,padding margin百分比按照父元素width作为参照物
- 4.9-4.10 矩阵乘法的性质 矩阵的幂运算 矩阵的转置及其性质
- jQuery特效,网站模板,商城模板,网页特效各种前端源码免费下载
- c语言的一颗会变色的圣诞树
- rsync+xinetd+inotify+sersync
- 阿里云Linux服务器如何打通网络
- ural 1998 The old Padawan