JAVA中写时复制(Copy-On-Write)Map实现
1,什么是写时复制(Copy-On-Write)容器?
写时复制是指:在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改。修改完成之后,将指向原来容器的引用指向新的容器(副本容器)。
2,写时复制带来的影响
①由于不会修改原始容器,只修改副本容器。因此,可以对原始容器进行并发地读。其次,实现了读操作与写操作的分离,读操作发生在原始容器上,写操作发生在副本容器上。
②数据一致性问题:读操作的线程可能不会立即读取到新修改的数据,因为修改操作发生在副本上。但最终修改操作会完成并更新容器,因此这是最终一致性。
3,在JDK中提供了CopyOnWriteArrayList类和CopyOnWriteArraySet类,但是并没有提供CopyOnWriteMap的实现。因此,可以参考CopyOnWriteArrayList自己实现一个CopyOnWriteHashMap
这里主要是实现 在写操作时,如何保证线程安全。
import java.util.Collection;
import java.util.HashMap;
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>(100);//初始大小应根据实际应用来指定
} @Override
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newMap = new HashMap<K, V>(internalMap);//复制出一个新HashMap
V val = newMap.put(key, value);//在新HashMap中执行写操作
internalMap = newMap;//将原来的Map引用指向新Map
return val;
}
} @Override
public void putAll(Map<? extends K, ? extends V> m) {
synchronized (this) {
Map<K, V> newMap = new HashMap<K, V>(internalMap);
newMap.putAll(m);
internalMap = newMap;
} } @Override
public V get(Object key) {
V result = internalMap.get(key);
return result;
}
......//other methods inherit from interface Map
}
从上可以看出,对于put() 和 putAll() 而言,需要加锁。而读操作则不需要,如get(Object key)。这样,当一个线程需要put一个新元素时,它先锁住当前CopyOnWriteMap对象,并复制一个新HashMap,而其他的读线程因为不需要加锁,则可继续访问原来的HashMap。
4,应用场景
CopyOnWrite容器适用于读多写少的场景。因为写操作时,需要复制一个容器,造成内存开销很大,也需要根据实际应用把握初始容器的大小。
不适合于数据的强一致性场合。若要求数据修改之后立即能被读到,则不能用写时复制技术。因为它是最终一致性。
总结:写时复制技术是一种很好的提高并发性的手段。
5,为什么会出现COW?
集合类(ArrayList、HashMap)上的常用操作是:向集合中添加元素、删除元素、遍历集合中的元素然后进行某种操作。当多个线程并发地对一个集合对象执行这些操作时就会引发ConcurrentModificationException,比如线程A在for-each中遍历ArrayList,而线程B同时又在删除ArrayList中的元素,就可能会抛出ConcurrentModificationException,可以在线程A遍历ArrayList时加锁,但由于遍历操作是一种常见的操作,加锁之后会影响程序的性能,因此for-each遍历选择了不对ArrayList加锁而是当有多个线程修改ArrayList时抛出ConcurrentModificationException,因此,这是一种设计上的权衡。
为了应对多线程并发修改这种情况,一种策略就是本文的主题“写时复制”机制;另一种策略是:线程安全的容器类:
ArrayList--->CopyOnWriteArrayList
HashMap--->ConcurrentHashMap
而ConcurrentHashMap并不是从“复制”这个角度来应对多线程并发修改,而是引入了分段锁(JDK7);CAS、锁(JDK11)解决多线程并发修改的问题。
参考资料:
Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
JAVA中写时复制(Copy-On-Write)Map实现的更多相关文章
- [转]QVector与QByteArray——Qt的写时复制(copy on write)技术
我们在之前的博文QVector的内存分配策略与再谈QVector与std::vector——使用装饰者让std::vector支持连续赋值中简单聊了聊QVector内存分配和赋值方面的一点东西,今天接 ...
- Java进阶知识点6:并发容器背后的设计理念 - 锁分段、写时复制和弱一致性
一.背景 容器是Java编程中使用频率很高的组件,但Java默认提供的基本容器(ArrayList,HashMap等)均不是线程安全的.当容器和多线程并发编程相遇时,程序员又该何去何从呢? 通常有两种 ...
- 写时复制集合 —— CopyOnWriteArrayList
前言 JUC 下面还有一个系列的类,都是 CopyOnWriteXXX ,意思是写时复制,这个究竟是怎么回事?那就以 CopyOnWriteArrayList 为切入点,一起了解写时复制是怎么回事? ...
- 写时复制和fork,vfork,clone
写时复制 原理: 用了“引用计数”,会有一个变量用于保存引用的数量.当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加,当有类析构时, ...
- 用户空间缺页异常pte_handle_fault()分析--(下)--写时复制【转】
转自:http://blog.csdn.net/vanbreaker/article/details/7955713 版权声明:本文为博主原创文章,未经博主允许不得转载. 在pte_handle_fa ...
- Rust写时复制Cow<T>
写时复制(Copy on Write)技术是一种程序中的优化策略,多应用于读多写少的场景.主要思想是创建对象的时候不立即进行复制,而是先引用(借用)原有对象进行大量的读操作,只有进行到少量的写操作的时 ...
- 线程不安全(Arraylist,HashSet,HashMap)和写时复制
package com.yangyuanyuan.juc1205; import java.util.List; import java.util.Map; import java.util.Set; ...
- phpCOW机制(写时复制)
写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入时才真正复制一份内存进行修改. COW最早应用在*nix系统中对线程与内存使用的优化,后面广泛的被使用在各种编程语言中,如C ...
- php 垃圾回收机制----写时复制和引用计数
PHP使用引用计数和写时复制来管理内存.写时复制保证了变量间复制值不浪费内存,引用计数保证了当变量不再需要时,将内存释放给操作系统. 要理解PHP内存管理,首先要理解一个概念----符号表. 符号表的 ...
随机推荐
- Mahout kmeans聚类
Mahout K-means聚类 一.Kmeans 聚类原理 K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一.K-means算法的基本思想是:以空间中k个点为中心进行聚 ...
- 《java入门第一季》之类StringBuffer类初步
/* * 线程安全(多线程分析) * 安全 -- 同步 -- 数据是安全的 * 不安全 -- 不同步 -- 效率高一些 * 安全和效率问题是永远困扰我们的问题. * 安全:医院的网站,银行网站 * 效 ...
- LeetCode之“链表”:在O(1)时间删除链表节点
下边讨论暂不包括尾节点. 一般来说,我们要删除链表中的一个节点是需要知道其上一节点的.但我们真的需要吗? 其实我们可以将待删节点的下一节点的值和指向的下一节点赋予待删节点,然后删除待删节点的下一节点. ...
- Java-ServletContextListener
/** * Implementations of this interface receive notifications about * changes to the servlet context ...
- mysql导入导出.sql数据
导入sql的命令:source "路径名"+/mytest_emp_dept.sql 常用source 命令 进入mysql数据库控制台, 如mysql -u root -p my ...
- 【uWSGI】实战之Django配置经验
uWSGI 是应用服务器,实现了uwsgi, wsgi等协议,可以运行wsgi 应用 uwsgi 是协议名 Django配置 下面是自己经常用的一个配置模板,基于1.9以上的版本使用的, 主要基于dj ...
- obj-c编程17:键值观察(KVO)
说完了前面一篇KVC,不能不说说它的应用KVO(Key-Value Observing)喽.KVO类似于ruby里的hook功能,就是当一个对象属性发生变化时,观察者可以跟踪变化,进而观察或是修正这个 ...
- OS X10.10下HomeBrew的安装提示
apple@kissAir: c_src$ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/m ...
- Nginx使用图片处理模块
Nginx可以编写很多额外的模块,这里我们需要按照能够通过URL响应返回缩放且含图片水印功能的模块. 1.安装一些使用过程中会用到的工具 yum install libgd2-devel yum in ...
- ip地址扫描
自己写的一个ip地址扫描脚本,功能是输入ip地址和掩码,通过ping检测整个网段的ip地址,输出ping的结果. 主要的几个函数如下: 1.ip地址转化为数值,方便计算 ip2num() { ip=$ ...