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

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

static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> 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 = ;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == )
tab = initTable();
else if ((f = tabAt(tab, i = (n - ) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(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 >= ) {
binCount = ;
for (Node<K,V> 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<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = ;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != ) {
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<K,V> entry = (Entry<K,V>)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更好。

源码分析--ConcurrentHashMap与HashTable(JDK1.8)的更多相关文章

  1. java读源码 之 list源码分析(ArrayList)---JDK1.8

    java基础 之 list源码分析(ArrayList) ArrayList: 继承关系分析: public class ArrayList<E> extends AbstractList ...

  2. 源码分析ConcurrentHashMap

    ConcurrentHashMap 1.7 segment分段锁 1.8 CAS 红黑树

  3. 史上最简单的的HashTable源码分析

    HashTable源码分析 1.前言 Hashtable 一个元老级的集合类,早在 JDK 1.0 就诞生了 1.1.摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap.Link ...

  4. JUC源码分析-集合篇(一)ConcurrentHashMap

    JUC源码分析-集合篇(一)ConcurrentHashMap 1. 概述 <HashMap 源码详细分析(JDK1.8)>:https://segmentfault.com/a/1190 ...

  5. JDK1.8中ArrayList的实现原理及源码分析

    一.概述 ArrayList是Java开发中使用比较频繁的一个类,通过对源码的解读,可以了解ArrayList的内部结构以及实现方法,清楚它的优缺点,以便我们在编程时灵活运用. 二.源码分析 2.1 ...

  6. LinkedHashMap源码分析

    hashMap源码分析:hashMap源码分析 版本说明:jdk1.7LinkedHashMap继承于HashMap,是一个有序的Map接口的实现.有序指的是元素可以按照一定的顺序排列,比如元素的插入 ...

  7. 【JUC】JDK1.8源码分析之ConcurrentHashMap(一)

    一.前言 最近几天忙着做点别的东西,今天终于有时间分析源码了,看源码感觉很爽,并且发现ConcurrentHashMap在JDK1.8版本与之前的版本在并发控制上存在很大的差别,很有必要进行认真的分析 ...

  8. Hashtable、ConcurrentHashMap源码分析

    Hashtable.ConcurrentHashMap源码分析 为什么把这两个数据结构对比分析呢,相信大家都明白.首先二者都是线程安全的,但是二者保证线程安全的方式却是不同的.废话不多说了,从源码的角 ...

  9. java基础系列之ConcurrentHashMap源码分析(基于jdk1.8)

    1.前提 在阅读这篇博客之前,希望你对HashMap已经是有所理解的,否则可以参考这篇博客: jdk1.8源码分析-hashMap:另外你对java的cas操作也是有一定了解的,因为在这个类中大量使用 ...

随机推荐

  1. centos6 安装mysql5.77(开发版)

    1. 配置yum源: [root@yyf ~]#rpm -Uvh http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm [ ...

  2. jmeter性能工具 使用手册(一)

    前置条件: 在jmeter官网下载jmter 安装包 电脑有java 环境 使用步骤: 打开jmeter 2.新建线程 Test plan--->add-->theads(users)-- ...

  3. Anacond的介绍

    Anacond的介绍 Anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依赖项. 因为包含了大量的科学包,Anaconda 的下载文件比较大( ...

  4. js把两个对象合并成一个对象

    Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象.它将返回目标对象 语法: Object.assign(target, ...sources)参数 targ ...

  5. size_t为什么重要

    参考:https://www.zhihu.com/question/24773728/answer/66535663 前言:使用size_t可能会提高代码的可移植性.有效性或者可读性,或许同时提高这三 ...

  6. iOS---如何截图,如何将图片保存到相册

    最近的项目中运用到了这两个功能,所以记录一下.做了一个小工程将两个方法结合到了一起 1 - (void)viewDidLoad { [super viewDidLoad]; UIButton * bt ...

  7. MongoDB与阿里云达成战略合作,最新数据库独家上线阿里云!

    11月26日,开源数据库厂商MongoDB与阿里云在北京达成战略合作,作为合作的第一步,最新版MongoDB 4.2数据库产品正式上线阿里云平台. 目前阿里云成为全球唯一可提供最新版MongoDB服务 ...

  8. CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心

    题目描述: 给定一个字符串 $S$ 给出 $Q$ 个操作,给出 $L,R,T$,求出字典序最小的 $S_{1}$ 为 $S[L...R]$的子串,且 $S_{1}$ 的字典序严格大于 $T$. 输出这 ...

  9. sublime px转rem插件

    http://www.cnblogs.com/phoebewang00/p/5593699.html lime 插件- px 转rem   一个CSS的px值转rem值的Sublime Text 3自 ...

  10. css3的各种属性的讲解

    1.渐变(gradients) 水平渐变:linear gradient 语法:background:linear-gradient(direction,color1,color2); directi ...