CocurrentHashMap和HashTable区别分析
集合是编程中最常用的数据结构。而谈到并发,几乎总是离不开集合这类高级数据结构的支持。比如两个线程需要同时访问一个中间临界区(Queue),比如常会用缓存作为外部文件的副本(HashMap)。这篇文章主要分析jdk1.5的3种并发集合类型(concurrent,copyonright,queue)中的ConcurrentHashMap,让我们从原理上细致的了解它们,能够让我们在深度项目开发中获益非浅。

| V get(Object key, int hash) { if (count != 0) { // read-volatile HashEntry e = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) { V v = e.value; if (v != null) return v; return readValueUnderLock(e); // recheck } e = e.next; } } return null; } |
| V readValueUnderLock(HashEntry e) { lock(); try { return e.value; } finally { unlock(); } } |
put操作一上来就锁定了整个segment,这当然是为了并发的安全,修改数据是不能并发进行的,必须得有个判断是否超限的语句以确保容量不足时能够rehash,而比较难懂的是这句int index = hash & (tab.length - 1),原来segment里面才是真正的hashtable,即每个segment是一个传统意义上的hashtable,如上图,从两者的结构就可以看出区别,这里就是找出需要的entry在table的哪一个位置,之后得到的entry就是这个链的第一个节点,如果e!=null,说明找到了,这是就要替换节点的值(onlyIfAbsent == false),否则,我们需要new一个entry,它的后继是first,而让tab[index]指向它,什么意思呢?实际上就是将这个新entry插入到链头,剩下的就非常容易理解了。
|
V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); try { int c = count; if (c++ > threshold) // ensure capacity rehash(); HashEntry[] tab = table; int index = hash & (tab.length - 1); HashEntry first = (HashEntry) tab[index]; HashEntry e = first; while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; V oldValue; if (e != null) { oldValue = e.value; if (!onlyIfAbsent) e.value = value; } else { oldValue = null; ++modCount; tab[index] = new HashEntry(key, hash, first, value); count = c; // write-volatile } return oldValue; } finally { unlock(); } } |
remove操作非常类似put,但要注意一点区别,中间那个for循环是做什么用的呢?(*号标记)从代码来看,就是将定位之后的所有entry克隆并拼回前面去,但有必要吗?每次删除一个元素就要将那之前的元素克隆一遍?这点其实是由entry的不变性来决定的,仔细观察entry定义,发现除了value,其他所有属性都是用final来修饰的,这意味着在第一次设置了next域之后便不能再改变它,取而代之的是将它之前的节点全都克隆一次。至于entry为什么要设置为不变性,这跟不变性的访问不需要同步从而节省时间有关,关于不变性的更多内容,请参阅之前的文章《线程高级---线程的一些编程技巧》
|
V remove(Object key, int hash, Object value) { lock(); try { int c = count - 1; HashEntry[] tab = table; int index = hash & (tab.length - 1); HashEntry first = (HashEntry)tab[index]; HashEntry e = first; while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; V oldValue = null; if (e != null) { V v = e.value; if (value == null || value.equals(v)) { oldValue = v; // All entries following removed node can stay // in list, but all preceding ones need to be // cloned. ++modCount; HashEntry newFirst = e.next; * for (HashEntry p = first; p != e; p = p.next) * newFirst = new HashEntry(p.key, p.hash, newFirst, p.value); tab[index] = newFirst; count = c; // write-volatile } } return oldValue; } finally { unlock(); } } |
|
static final class HashEntry { final K key; final int hash; volatile V value; final HashEntry next; HashEntry(K key, int hash, HashEntry next, V value) { this.key = key; this.hash = hash; this.next = next; this.value = value; } } |
以上,分析了几个最简单的操作,限于篇幅,这里不再对rehash或iterator等实现进行讨论,有兴趣可以参考src。
接下来实际上还有一个疑问,ConcurrentHashMap跟HashMap相比较性能到底如何。这在Brian Goetz的文章中已经有过评测http://www.ibm.com/developerworks/cn/java/j-jtp07233/。
CocurrentHashMap和HashTable区别分析的更多相关文章
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)
HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别 文章来源:http://www.cnblogs.com/beatIteWeNerverGiveU ...
- C++中关于[]静态数组和new分配的动态数组的区别分析
这篇文章主要介绍了C++中关于[]静态数组和new分配的动态数组的区别分析,很重要的概念,需要的朋友可以参考下 本文以实例分析了C++语言中关于[]静态数组和new分配的动态数组的区别,可以帮助大家加 ...
- Java中Comparable和Comparator接口区别分析
Java中Comparable和Comparator接口区别分析 来源:码农网 | 时间:2015-03-16 10:25:20 | 阅读数:8902 [导读] 本文要来详细分析一下Java中Comp ...
- Oracle nvchar2和varchar2区别分析
Oracle nvchar2和varchar2区别分析: [注意]VARCHAR2是Oracle提供的特定数据类型,Oracle可以保证VARCHAR2在任何版本中该数据类型都可以向上和向下兼容.VA ...
- jQuery中的.bind()、.live()和.delegate()之间区别分析
jQuery中的.bind()..live()和.delegate()之间区别分析,学习jquery的朋友可以参考下. DOM树 首先,可视化一个HMTL文档的DOM树是很有帮助的.一个简单的 ...
- jQuery中的bind() live() delegate()之间区别分析
jQuery中的bind() live() delegate()之间区别分析 首先,你得要了解我们的事件冒泡(事件传播)的概念,我先看一张图 1.bind方式 $('a').bind('click', ...
- HashMap和HashTable区别
HashMap和HashTable区别 HashMap--->允许控制/线程安全 HashTable-->线程不安全
- Java基础知识强化之集合框架笔记66:Map集合面试题之HashMap和Hashtable区别(重要)
1. HashMap和Hashtable区别 ? • Hashtable:线程安全,效率低.不允许null键和null值 • HashMap:线程不安全,效率高.允许null键和null值 packa ...
- addEventListener()及attachEvent()区别分析
Javascript 的addEventListener()及attachEvent()区别分析 Mozilla中: addEventListener的使用方式: target.addEventLis ...
随机推荐
- 【BZOJ】2054: 疯狂的馒头
[题意]给定n个元素,m次给一段区间染色为i,求最终颜色. [算法]并查集 [题解]因为一个点只受最后一次染色影响,所以倒过来每次将染色区间用并查集合并,父亲指向最右边的点. 细节: 1.fa[n+1 ...
- bzoj 3207 可持久化线段树
首先因为固定询问长度,所以我们可以将整个长度为n的数列hash成长度为n-k+1的数列,每次询问的序列也hash成一个数,然后询问这个数是不是在某个区间中出现过,这样我们可以根据初始数列的权值建立可持 ...
- 原生ES-Module在浏览器中的尝试
其实浏览器原生模块相关的支持也已经出了一两年了(我第一次知道这个事情实在2016年下半年的时候) 可以抛开webpack直接使用import之类的语法 但因为算是一个比较新的东西,所以现在基本只能自己 ...
- RecycleView Bug:java.lang.IndexOutOfBoundsException: Inconsistency detected.
今天使用RecyclerView时,上下两个RecyclerView,在实现下拉刷新时,报错: java.lang.IndexOutOfBoundsException: Inconsistency d ...
- 浅谈JobExecutionContext与JobDataMap
1.JobExecutionContext简介 (1)当Scheduler调用一个Job,就会将JobExecutionContext传递给job的execute方法 quartz无法调用job的有参 ...
- GBK UTF-16 UTF-8 编码表
GBK UTF-16 UTF-8 ================== D2BB 4E00 E4 B8 80 一 B6A1 4E01 E4 B8 81 丁 C6DF 4E03 E4 ...
- sublime3插件安装及报错处理
ctrl+shift+p调用出窗口:输入install package,然后输入想安装的插件. 有些用户安装的可能是国内破解版的,我的就是,然后install package报错: Package C ...
- tornado简单使用
这篇适用于快速上手想了解更深:http://www.tornadoweb.cn/ https://tornado-zh.readthedocs.io/zh/latest/ Tornado 是 Fr ...
- make command explaination 編譯命令解釋
Creating .config file make ARCH=arm CROSS_COMPILE=arm-none-eabi- stm32_defconfig 以上命令是 將變數 ARCH=arm, ...
- python基础===Number
本文转自:python之Number 1.Python number数字 Python Number 数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变 Number 数据类型的值,将 ...