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内存管理,首先要理解一个概念----符号表. 符号表的 ...
随机推荐
- Warning: $HADOOP_HOME is deprecated解决方法
启动Hadoop时报了一个警告信息,我的系统是CentOS,安装的Hadoop版本是hadoop1.2.1,具体警告信息如下: [root@m1 hadoop-1.2.1]# bin/start-al ...
- Esper剖析
Esper剖析 最近在看论文,发现文中有些语言自己未曾见过,经过一番搜索,才发觉是自己接触到了新知识. 官网: )esper的核心包包含了EPL语法解析引擎,事件监听机制,事件处理等核心模块. (2) ...
- 内存数据网格hazelcast的一些机制原理
hazelcast作为一个内存数据网格工具,还算比较优秀,听说有Apache顶级项目使用它,值得研究下,使用文档可以直接看官方文档,但机制原理相关的资料基本没有,本人硬撸源码写的一些东西,跟大家分享一 ...
- 如何成为Android高手
要成为Android 高手并不是一件容易的事情.并不是很多人想象的 能够飞快的写出几行漂亮的代码去解决一些困难的问题 就是Android 高手了.真正的Android 高手需要考虑的问题远远不是写些漂 ...
- 《java入门第一季》之面向对象(static关键字内存图解)
- OpenCV 闭合轮廓检测
这个好像是骨头什么的,但是要求轮廓闭合,于是对图片进行一下膨胀操作,再次检测轮廓就好了. // A closed contour.cpp : 定义控制台应用程序的入口点. // #include &q ...
- SpriteBuilder中锚点的一般用法
注意:改变节点的锚点(anchor point)将会影响缩放和旋转操作,也会影响边界边框和碰撞的检测. 锚点仅仅挪动节点的视觉表现,这种改变可能与物理表现不一致. 你绝不应该错误的挪动锚点去改变节点的 ...
- Smali语法汇总(一)
Opcode 操作码(hex) Opcode name 操作码名称 Explanation 说明 Example 示例 00 nop 无操作 0000 - nop 01 move vx, vy 移动v ...
- myeclipse不编译
错误: org.eclipse.core.internal.registry.configurationElementHandle cannot be cast to org.eclipse.jdt. ...
- Srping mvc mabatis 报错 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
我的Mapper采用接口+注解的方式注入 @Repository(value="customerServOutCallMapper")public interface Custom ...