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内存管理,首先要理解一个概念----符号表. 符号表的 ...
随机推荐
- Java-clone总结
纸上得来终觉浅,绝知此事要躬行 --陆游 问渠那得清如许,为有源头活水来 --朱熹 clone和Copy:当有一个对象Person p1=new Person("name" ...
- 非阻塞IO模式原理
与阻塞模式对应的另一种模式叫非阻塞IO模式,在整个通信过程中读和写操作不会阻塞,当前处理线程不存在阻塞情况.从A机器到B机器它的通信过程是:A机器一条线程将通道设置为写事件后往下执行,而另外一条线程遍 ...
- umask函数的用法 - 如何进行权限位的设置
下面程序创建了两个文件,创建foo文件时,umask值为0,创建第二个时,umask值禁止所有组和其他用户的访问权限. 测试结果: 测试结果可以看出更改进程的文件模式掩码并不影响其父进程(常常是she ...
- 队列顺序存储 - 设计与实现 - API函数
队列是一种特殊的线性表 队列仅在线性表的两端进行操作 队头(Front):取出数据元素的一端 队尾(Rear):插入数据元素的一端 队列不允许在中间部位进行操作! queue常用操作 销毁队列 清空队 ...
- java数据类型易错点简单总结,欢迎大神前辈补充!谢谢
数据类型那这边看似简单,花了我很长时间也就是才练到几成"功力"吧.还希望路过的大神在下面补充,菜鸟的我深受感谢! 首先看两个思考题 思考题1:请问下面这个有没有问题 double ...
- iOS开发常用第三方库
UI 动画 网络相关 Model 其他 数据库 缓存处理 PDF 图像浏览及处理 摄像照相视频音频处理 响应式框架 消息相关 版本新API的Demo 代码安全与密码 测试及调试 AppleWatch ...
- 点击劫持漏洞之理解 python打造一个挖掘点击劫持漏洞的脚本
前言: 放假了,上个星期刚刚学习完点击劫持漏洞.没来的及写笔记,今天放学总结了一下 并写了一个检测点击劫持的脚本.点击劫持脚本说一下哈.= =原本是打算把网站源码 中的js也爬出来将一些防御的代码匹配 ...
- 多重影分身——C#中多线程的使用二(争抢共享资源)
只要服务器承受得了,我们可以开任意个线程同时工作以提高效率,然而 两个线程争抢资源可能导致数据混乱. 例如: public class MyFood { public static int Last ...
- Netty入门
一.NIO Netty框架底层是对NIO的高度封装,所以想要更好的学习Netty之前,应先了解下什么是NIO - NIO是non-blocking的简称,在jdk1.4 里提供的新api,他的他的特性 ...
- python redis模块的常见的几个类 Redis 、StricRedis和ConnectionPool
日常写代码过程中,经常需要连接redis进行操作.下面我就介绍下python操作redis模块redis中的几个常见类,包括redis连接池. 一.StrictRedis 类 请看代码:. #!/us ...