CurrentHashMap详解
一、简单介绍
CurrentHashMap是线程安全的键值对容器,支持并发操作,外围由一个segment数组组成,每个segment是一个锁;每个segment内部是一个数组加链表的结构。
二、CurrentHashMap结构分析
三、源码解读
1、属性
/** * The default initial capacity for this table, * used when not otherwise specified in a constructor. */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** * The default load factor for this table, used when not * otherwise specified in a constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * The default concurrency level for this table, used when not * otherwise specified in a constructor. */ static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** * The maximum capacity, used if a higher value is implicitly * specified by either of the constructors with arguments. MUST * be a power of two <= 1<<30 to ensure that entries are indexable * using ints. */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * The maximum number of segments to allow; used to bound * constructor arguments. */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative /** * Number of unsynchronized retries in size and containsValue * methods before resorting to locking. This is used to avoid * unbounded retries if tables undergo continuous modification * which would make it impossible to obtain an accurate result. */ static final int RETRIES_BEFORE_LOCK = 2; /* ---------------- Fields -------------- */ /** * Mask value for indexing into segments. The upper bits of a * key's hash code are used to choose the segment. */ final int segmentMask; /** * Shift value for indexing within segments. */ final int segmentShift; /** * The segments, each of which is a specialized hash table */ final Segment<K,V>[] segments; transient Set<K> keySet; transient Set<Map.Entry<K,V>> entrySet; transient Collection<V> values;
属性名 | 修饰符 | 值 | 用途 |
DEFAULT_INITIAL_CAPACITY | static final int | 16 | Map默认容量,capacity指所有segement数组大小之和 |
DEFAULT_LOAD_FACTOR | static final float | 0.75f | Map默认加载因子 |
DEFAULT_CONCURRENCY_LEVEL | static final int | 16 | 默认并发数,即segement数组大小 |
MAXIMUM_CAPACITY | static final int | 1 << 30 | 最大容量限制 |
MAX_SEGMENTS | static final int | 1 << 16 | 最大segment |
RETRIES_BEFORE_LOCK | static final int | 2 | ? |
segmentMask | final int | segement数组大小-1 | 定位到segment下标的掩码,其实是对hash值的“&”运算,因为segement大小为2的幂,“&”相当于取膜。 |
segmentShift | final int | 32-segement幂值 | 定位到segment下标时,用到,对hash值向右移位,我理解,这样就会对高位取膜。不知道为什么要对高位取膜? |
segments | Segment<K,V>[] | 段,用来存储hashEntry数组的容器。 | |
2、内部数据结构
类名 | 用途 | 注意 |
Segment | 段,一个段是一个锁,段的个数就是可并发数,ConcurrentHashMap由segement数组组成。 | |
HashEntry | 段内由HashEntry数组组成 | HashEntry的next属性是final的,插入只能从头部开始插入,删除的时候,需要复制删除节点前面的节点,不能修改next值。为了写一致? |
3、主要方法
序号 | 方法名 | 作用 | 注意项 | 疑问 |
1 | put(K key, V value) | 向map里面添加元素,主要看segement的put方法。 |
1、value不能为null 2、使用时对segement会加锁。 |
|
putIfAbsent(K key, V value) | 如果key存在不用传入value覆盖oldValue | |||
rehash() |
segement中某个链表大小大于阈值,数组长度乘以2。 |
rehash方法会遍历旧的数组,先找到链表最后一个节点,然后从头遍历clone到新的数组中,因为HahsEntry的next属性是final的所以只能clone,如果我来做会直接遍历链表所有节点克隆到新的数组中,但是源码是先找到最后一个节点然后clone最后一个之上的所有节点,最后一个节点先放到数组中,这样做的好处是节省了空间,旧数组上每个链表最后一个节点不用克隆,坏处是链表需要遍历两次。不知道为什么作者这么选择? | ||
2 | get(Object key) | 根据key值获取Value |
1、不用加锁 2、如果取到null需要加锁重取 |
如果取到null需要加锁重取? |
3 | remove(Object key) | 根据key值删除节点 |
1、加锁 2、删除节点后,节点前的节点会倒序。 |
|
4 | size() | 获取map元素个数 |
1、size会查两遍,两遍不一样,就重试一次,一样直接退出。如果重试也不一致,则加锁重试。 2、有序加锁 |
|
5 | containsKey(Object key) | 查看指定对象是否是map中的key | 1、不加锁 | |
6 | containsValue(Object value) | map中存在指定value返回true |
1、value 不允许为null 2、遍历map,如果检测到value存在则返回true,如果不存在,查看是否有变动,如果有变动再查一次,还查不到,并且有变动,则加锁再查一次。 |
|
(1)put
先算出hash值,根据hash值定位到segement,再用segement的put方法设置值。
/** * Maps the specified key to the specified value in this table. * Neither the key nor the value can be null. * * <p> The value can be retrieved by calling the <tt>get</tt> method * with a key that is equal to the original key. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt> * @throws NullPointerException if the specified key or value is null */ public V put(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false); }
segement.put
V put(K key, int hash, V value, boolean onlyIfAbsent) {
lock();
try {
int c = count;
if (c++ > threshold) // ensure capacity
rehash();
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
HashEntry<K,V> first = tab[index];
HashEntry<K,V> 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<K,V>(key, hash, first, value);
count = c; // write-volatile
}
return oldValue;
} finally {
unlock();
}
}
void rehash() {
HashEntry<K,V>[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity >= MAXIMUM_CAPACITY)
return;
/*
* Reclassify nodes in each list to new Map. Because we are
* using power-of-two expansion, the elements from each bin
* must either stay at same index, or move with a power of two
* offset. We eliminate unnecessary node creation by catching
* cases where old nodes can be reused because their next
* fields won't change. Statistically, at the default
* threshold, only about one-sixth of them need cloning when
* a table doubles. The nodes they replace will be garbage
* collectable as soon as they are no longer referenced by any
* reader thread that may be in the midst of traversing table
* right now.
*/
HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
threshold = (int)(newTable.length * loadFactor);
int sizeMask = newTable.length - 1;
for (int i = 0; i < oldCapacity ; i++) {
// We need to guarantee that any existing reads of old Map can
// proceed. So we cannot yet null out each bin.
HashEntry<K,V> e = oldTable[i];
if (e != null) {
HashEntry<K,V> next = e.next;
int idx = e.hash & sizeMask;
// Single node on list
//next为null直接设置到新数组上去,新数组上只会比原数组组员少。所以如果单节点肯定在新数组上还是单节点。
if (next == null)
newTable[idx] = e;
else {
// Reuse trailing consecutive sequence at same slot
HashEntry<K,V> lastRun = e;
int lastIdx = idx;
for (HashEntry<K,V> last = next;
last != null;
last = last.next) {
int k = last.hash & sizeMask;
if (k != lastIdx) {
lastIdx = k;
lastRun = last;
}
}
newTable[lastIdx] = lastRun;
// Clone all remaining nodes
for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
int k = p.hash & sizeMask;
HashEntry<K,V> n = newTable[k];
newTable[k] = new HashEntry<K,V>(p.key, p.hash,
n, p.value);
}
}
}
}
table = newTable;
}
相关推荐
端口详解 端口详解 端口详解 端口详解 端口详解
dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解dm分区详解...
路由配置路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解及案例路由配置详解...
在udhcp源码详解(一)中,可能会详细介绍这些模块的基本功能和相互关系。配置文件解析是udhcp启动时的关键步骤,它读取用户的配置参数,如IP地址池、租约时间等。内存管理则涉及到如何有效地存储和检索DHCP请求和...
gpiogpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用实例gpio详解及应用...
C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++编程实例详解C++...
MATLAB通信仿真及应用实例详解pdf-MATLAB通信仿真及应用实例详解.part03.rar 论坛里有兄弟发过了。但是出了问题,这次补充一个完整的。 MATLAB通信仿真及应用实例详解.part09.rar ...
本人收集的EXT详解,EXT开发中组件详解
gcc参数详解 gcc参数详解 gcc参数详解 gcc参数详解
cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置...
Labview实用工具详解源码第三部分,压缩包比较大,分八个上传。
上传限制无奈分卷压缩 一共12卷 要12卷在同目录才可以解压 给大家带来不便请你们谅解 VC++深入详解pdf版 VC++深入详解 VC++深入详解电子档
TCP/IP详解系列是由W. Richard Stevens撰写的经典网络技术书籍,包括《TCP/IP详解 卷一:协议》、《TCP/IP详解 卷二:实现》和《TCP/IP详解 卷三:TCP事务》。这套书深入浅出地阐述了TCP/IP协议族的各个层面,是网络...
BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解BMP文件格式详解
xshellxshell详解及实际案例分析xshell详解及实际案例分析xshell详解及实际案例分析xshell详解及实际案例分析xshell详解及实际案例分析xshell详解及实际案例分析xshell详解及实际案例分析xshell详解及实际案例分析...
MATLAB通信仿真及应用实例详解pdf-MATLAB通信仿真及应用实例详解.part08.rar 论坛里有兄弟发过了。但是出了问题,这次补充一个完整的。 MATLAB通信仿真及应用实例详解.part09.rar ...
MATLAB通信仿真及应用实例详解pdf-MATLAB通信仿真及应用实例详解.part07.rar 论坛里有兄弟发过了。但是出了问题,这次补充一个完整的。 MATLAB通信仿真及应用实例详解.part09.rar ...
液晶驱动详解(想写驱动的朋友请进!).zip液晶驱动详解(想写驱动的朋友请进!).zip液晶驱动详解(想写驱动的朋友请进!).zip液晶驱动详解(想写驱动的朋友请进!).zip液晶驱动详解(想写驱动的朋友请进!).zip液晶驱动详解(想...
MATLAB通信仿真及应用实例详解pdf-MATLAB通信仿真及应用实例详解.part05.rar 论坛里有兄弟发过了。但是出了问题,这次补充一个完整的。 MATLAB通信仿真及应用实例详解.part09.rar ...
c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 c语言指针详解 ...