没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
试读
16页
Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMapJava——并发容器之ConcurrentHashMap
资源推荐
资源详情
资源评论
并发容器之 ConcurrentHashMap(JDK
1.8 版本)
1.ConcurrentHashmap 简介
在使用 HashMap 时在多线程情况下扩容会出现 CPU 接近 100%的情况,因为
hashmap 并不是线程安全的,通常我们可以使用在 java 体系中古老的
hashtable 类,该类基本上所有的方法都采用 synchronized 进行线程安全的控制,
可想而知,在高并发的情况下,每次只有一个线程能够获取对象监视器锁,这样
的并发性能的确不令人满意。另外一种方式通过 Collections 的 Map<K,V>
synchronizedMap(Map<K,V> m)将 hashmap 包装成一个线程安全的 map。比如
SynchronzedMap 的 put 方法源码为:
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
实际上 SynchronizedMap 实现依然是采用 synchronized 独占式锁进行线程安
全的并发控制的。同样,这种方案的性能也是令人不太满意的。针对这种境况,
Doug Lea 大师不遗余力的为我们创造了一些线程安全的并发容器,让每一个
java 开发人员倍感幸福。相对于 hashmap 来说,ConcurrentHashMap 就是线程
安全的 map,其中利用了锁分段的思想提高了并发度。
ConcurrentHashMap 在 JDK1.6 的版本网上资料很多,有兴趣的可以去看看。
JDK 1.6 版本关键要素:
1. segment 继承了 ReentrantLock 充当锁的角色,为每一个 segment 提供了线程安全
的保障;
2. segment 维护了哈希散列表的若干个桶,每个桶由 HashEntry 构成的链表。
而到了 JDK 1.8 的 ConcurrentHashMap 就有了很大的变化,光是代码量就足足
增加了很多。1.8 版本舍弃了 segment,并且大量使用了 synchronized,以及 CAS
无锁操作以保证 ConcurrentHashMap 操作的线程安全性。至于为什么不用
ReentrantLock 而是 Synchronzied 呢?实际上,synchronzied 做了很多的优化,
包括偏向锁,轻量级锁,重量级锁,可以依次向上升级锁状态,但不能降级(关
于 synchronized 可以看这篇文章[2]),因此,使用 synchronized 相较于
ReentrantLock 的性能会持平甚至在某些情况更优,具体的性能测试可以去网上
查阅一些资料。另外,底层数据结构改变为采用数组+链表+红黑树的数据形式。
2.关键属性及类
在了解 ConcurrentHashMap 的具体方法实现前,我们需要系统的来看一下几个
关键的地方。
ConcurrentHashMap 的关键属性
table volatile Node<K,V>[] table://装载 Node 的数组,作为
ConcurrentHashMap 的数据容器,采用懒加载的方式,直到第一次插入数
据的时候才会进行初始化操作,数组的大小总是为 2 的幂次方。
nextTable volatile Node<K,V>[] nextTable; //扩容时使用,平时为 null,只
有在扩容的时候才为非 null
sizeCtl volatile int sizeCtl; 该属性用来控制 table 数组的大小,根据是否初
始化和是否正在扩容有几种情况: **当值为负数时:**如果为-1 表示正
在初始化,如果为-N 则表示当前正有 N-1 个线程进行扩容操作; **当
值为正数时:**如果当前数组为 null 的话表示 table 在初始化过程中,
sizeCtl 表示为需要新建数组的长度; 若已经初始化了,表示当前数据容
器(table 数组)可用容量也可以理解成临界值(插入节点数超过了该临
界值就需要扩容),具体指为数组的长度 n 乘以 加载因子 loadFactor;
当值为 0 时,即数组长度为默认初始值。
sun.misc.Unsafe U 在 ConcurrentHashMapde 的实现中可以看到大量的
U.compareAndSwapXXXX 的方法去修改 ConcurrentHashMap 的一些属性。
这些方法实际上是利用了 CAS 算法保证了线程安全性,这是一种乐观策
略,假设每一次操作都不会产生冲突,当且仅当冲突发生的时候再去尝试。
而 CAS 操作依赖于现代处理器指令集,通过底层 CMPXCHG 指令实现。
CAS(V,O,N)核心思想为:若当前变量实际值 V 与期望的旧值 O 相同,则
表明该变量没被其他线程进行修改,因此可以安全的将新值 N 赋值给变
量;若当前变量实际值 V 与期望的旧值 O 不相同,则表明该变量已经
被其他线程做了处理,此时将新值 N 赋给变量操作就是不安全的,在进
行重试。而在大量的同步组件和并发容器的实现中使用 CAS 是通过
sun.misc.Unsafe 类实现的,该类提供了一些可以直接操控内存和线程的
底层操作,可以理解为 java 中的“指针”。该成员变量的获取是在静态代
码块中:
static {
try {
U = sun.misc.Unsafe.getUnsafe(); ....... }
catch (Exception e) {
throw new Error(e);
}
}
ConcurrentHashMap 中关键内部类
Node Node 类实现了 Map.Entry 接口,主要存放 key-value 对,并且具
有 next 域
static class Node<K,V> implements Map.Entry<K,V>
{ final int hash; final K
key; volatile V val;
volatile Node<K,V> next; ......
}
另外可以看出很多属性都是用 volatile 进行修饰的,也就是为了保证内存可见性。
TreeNode 树节点,继承于承载数据的 Node 类。而红黑树的操作是针对
TreeBin 类的,从该类的注释也可以看出,也就是 TreeBin 会将 TreeNode
进行再一次封装
剩余15页未读,继续阅读
资源评论
Andy&lin
- 粉丝: 97
- 资源: 214
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功