热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:源码分析ConcurrentHashMap与HashTable(JDK1.8)

本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。Concu
本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。


  ConcurrentHashMap和Hashtable都是线程安全的K-V型容器。本篇从源码入手,简要说明它们两者的实现原理和区别。

 

  与HashMap类似,ConcurrentHashMap底层也是以数组+链表+红黑树实现的,以Node节点封装K-V和hash。


static class Node implements Map.Entry {
final
int hash;
final K key;
volatile V val;
volatile Node next;

  val和next以volatile关键字进行修饰,保证内存可见性。

看一下它的put()方法:


final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node[] tab = table;;) {
Node
f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab
= initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab
= helpTransfer(tab, f);
else {
V oldVal
= null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount
= 1;
for (Node e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek
= e.key) == key ||
(ek
!= null && key.equals(ek)))) {
oldVal
= e.val;
if (!onlyIfAbsent)
e.val
= value;
break;
}
Node
pred = e;
if ((e = e.next) == null) {
pred.next
= new Node(hash, key,
value,
null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node
p;
binCount
= 2;
if ((p = ((TreeBin)f).putTreeVal(hash, key,
value))
!= null) {
oldVal
= p.val;
if (!onlyIfAbsent)
p.val
= value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(
1L, binCount);
return null;
}



  • 与HashMap不同,不允许空键值

  • 计算hashCode

  • 判断是否初始化

  • 如果当前位置为空,利用CAS算法,放置节点

  • 如果当前hashCode == MOVED,进行扩容

  • 利用synchronized锁,进行链表或者红黑树的节点放置

  • 链表数量大于8,转为红黑树

ConcurrentHashMap的get()方法没有使用同步锁机制。

JDK1.8以后,ConcurrentHashMap的线程安全都是利用CAS + synchronized来实现的。效率较高。

 

对于HashTable,它底层为数组+链表结构,也不允许空键值。以Entry封装K-V和hash。

主要get()和put()方法:


public synchronized V get(Object key) {
Entry
tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings(
"unchecked")
Entry
entry = (Entry)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old
= entry.value;
entry.value
= value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}

  HashTable不仅因为没有红黑树,对于数据遍历的效率就比较低,而且在get()方法都加了synchronized关键字,而且get()和put()方法都是直接加在方法上。这样一来效率就比ConcurrentHashMap低得多了。所以,如果要在并发情况下使用K-V容器,使用ConcurrentHashMap更好。

 


推荐阅读
author-avatar
小梅LMY
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有