ConcurrentHashMap 从Java7 到 Java8的改变
一、关于分段锁
1.分段锁发展概况
集合框架很大程度减少了java程序员的重复劳动。在Java多线程环境中,以线程安全的方式使用集合类是一个首先考虑的问题。
能够保证线程安全的哈希表中,ConcurrentHashMap是大家都熟知的,也知道它内部使用了分段锁。然而,进入到Java8时代,分段锁成为了历史。
2.新版本ConcurrentHashMap
在Java8的ConcurrentHashMap中,分段锁仅用来处理对象流。
Java7中,Segment继承于ReentrantLock使用了显示锁,在Segment的实例方法中,每个更新操作内部又使用Unsafe来处理更新。这显然是一种浪费。显示锁、Unsafe 这二者都是可以保证对对象的原子操作。使用一个即可。
Java7中的数据存储在数组 final Segment<K,V>[] segments; 这个一个特定大小的Segment数组,
Segment继承于ReentrantLock 故而可以在更新操作时使用显示锁。
二、Java7的ConcurrentHashMap
java7中,ConcurrentHashMap的内部类Segment.
Segment继承了ReetrantLock,利用了显示锁,同时在更新操作中也使用了Unsafe.双管齐下来保证线程安全。
static final class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
// tryLock()最多等待时间
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
// 分段表,元素通过entryAt/setEntryAt这两个方法提供了瞬时操作。
transient volatile HashEntry<K,V>[] table;
// 元素数量,通过锁或可见性地瞬时读取
transient int count;
// 修改次数
transient int modCount;
// 表大小超出threshold时,重新哈希运算。它的值 = (int)(capacity*loadFactor)
transient int threshold;
// 负载因子
final float loadFactor;
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}
/**
移除:value为null时,只匹配key,否则,两个都匹配
*/
final V remove(Object key, int hash, Object value) {
if (!tryLock())
scanAndLock(key, hash);
V oldValue = null;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> e = entryAt(tab, index);
HashEntry<K,V> pred = null;
while (e != null) {
K k;
HashEntry<K,V> next = e.next;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
V v = e.value;
if (value == null || value == v || value.equals(v)) {
if (pred == null)
// 这里是cas操作
setEntryAt(tab, index, next);
else
pred.setNext(next);
++modCount;
--count;
oldValue = v;
}
break;
}
pred = e;
e = next;
}
} finally {
unlock();
}
return oldValue;
}
final void clear() {
lock();
try {
HashEntry<K,V>[] tab = table;
for (int i = 0; i < tab.length ; i++)
// Unsafe 操作
setEntryAt(tab, i, null);
++modCount;
count = 0;
} finally {
unlock();
}
}
}
三、Java8的ConcurrentHashMap
Java8中,ConcurrentHashMap较之前版本有了很大的改变。
使用Node数组替代了Segment数组来存储数据。Node数组中不再使用显示锁,而是Unsafe的乐观锁机制。
Segment予以保留,仅用来处理对象流的读写。
从如下Java8版本的ConcurrentHashMap$Segment源码来看,分段锁,基本弃用了。
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
ConcurrentHashMap 从Java7 到 Java8的改变的更多相关文章
- Java7与Java8中的HashMap和ConcurrentHashMap知识点总结
JAVA7 Java7的ConcurrentHashMap里有多把锁,每一把锁用于其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率呢.这 ...
- java7,java8 中HashMap和ConcurrentHashMap简介
一:Java7 中的HashMap 结构: HashMap 里面是一个数组,然后数组中每个元素是一个单向链表.链表中每个元素称为一个Entry 实例,Entry 包含四个属性:key, value, ...
- Java7 和 Java8 中的 ConcurrentHashMap 原理解析
Java7 中 ConcurrentHashMap ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些. 整个 ConcurrentHash ...
- java5、java6、java7、java8的新特性
Java5: 1.泛型 Generics: 引用泛型之后,允许指定集合里元素的类型,免去了强制类型转换,并且能在编译时刻进行类型检查的好处. Parameterized Type作为参数 ...
- java7与java8的新特性
java7 新特性: 1. switch 里面的 case 条件可以使用字符串了. 2. 运用 List\tempList = new ArrayList<>(); 即泛型实例化类型自动判 ...
- java7和java8新特性
以下来至网址: http://blog.csdn.net/samjustin1/article/details/52268004 Java7 新特性 1.switch中可以使用字符串了 String ...
- java7与java8中计算两个日期间隔多少年多少月多少天的实现方式
最近工作中碰到个新需求,计算每个员工入职公司的时长,要求形式为多少年多少月多少天形式,某个值为0就跳过不显示,因为前段时间学习过java8新特性,对于这个需求,java8的新时间日期API可以直接解决 ...
- 配置java环境变量,实现一条命令自由切java7 或java8
在多个java编译环境中,有时需要java 7,有时又需要java 8,怎么配置java 环境,可以快速自动切换呢?下面用mac演示在 /etc/bashrc 中配置的环境变量 # 设置 JDK ex ...
- 关于HashSet在 java7 与 java8的不同
作者:RednaxelaFX链接:https://www.zhihu.com/question/28414001/answer/40733996来源:知乎著作权归作者所有.商业转载请联系作者获得授权, ...
随机推荐
- Django框架中的model(操作数据库)
什么是ORM ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复 ...
- Nova控制节点集群
#Nova控制节点集群 openstack pike 部署 目录汇总 http://www.cnblogs.com/elvi/p/7613861.html ##Nova控制节点集群 # control ...
- ssh免密验证,shell批量设置
ssh免密验证,shell批量设置 #ssh免密验证,shell自动设置 echo '#!/bin/sh . /etc/init.d/functions [[ -f /usr/bin/expect ] ...
- SQL Server Profiler的使用
最近一个项目,使用微软的Entity Framework的ORM框架的项目,部署到现场后,出现了系统缓慢,多个客户端的内存溢出崩溃的问题. 打开了SQL Server Profiler排查,发现有全表 ...
- 200行Java代码搞定计算器程序
发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树,哈哈;) 只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~ 代码点评: 从朴素的界面配色到简单易懂错误提示 ...
- 使用.Net Core+EF7 完成CodeFirst
emmm,本来想着用Core做一个小项目玩玩的,然后肯定是要用到数据库的, 然后想,啊,要不用CodeFirst,感觉很腻害的样子,于是,一脸天真无邪的我就踏入了一个深不见底的天坑... 本来想着,应 ...
- SQL-Oracle内实现柱形图式的效果
在SQL SERVER内有一个函数replicate()可以实现柱形图效果,本质上是利用字符重复出现的次数来控制柱形图的长短,效果如图: 如果要在Oracle内实现相同的效果,则需要自己写一个函数: ...
- Ubuntu使用之Svn命令小技巧
注: [svn Path]:是指要代替码分支的server绝对路径 [Path]:是指终端相对当前文件夹的相对路径.假设是在当前文件夹下,就省略路径 ①.取svnserver的代码: svn co [ ...
- poj 2034 Anti-prime Sequences(dfs)
//相邻的 2.3......d 之和都要不为素数 # include <algorithm> # include <stdio.h> using namespace std; ...
- AntiXSS的使用
下载类库: http://wpl.codeplex.com 添加程序集引用 在web.config文件中将AntiXSS类库注册为应用程序的编码器 在<system.web& ...