此系列文章放在了我的专栏里, 欢迎查看
https://blog.csdn.net/column/details/24187.html
相关衔接
从源码看Android常用的数据结构 ( SDK23版本 ) ( 一 , 总述 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 二, List篇 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 三 , Queue篇)
从源码看Android常用的数据结构 ( SDK23版本 ) ( 四, Set篇 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 五, Map篇 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 六, ConcurrentHashMap )
从源码看Android常用的数据结构 ( 七, SDK28下的HashMap )
Github里有一份Android使用到的小技术, 欢迎查看:
https://github.com/YouCii/LearnApp
前言
由于从源码看Android常用的数据结构 ( SDK23版本 ) ( 五, Map篇 ) 篇幅已经很长了, ConcurrentHashMap 要看的又比较多, 所以单独放在这里.
ConcurrentHashMap
致力于高效率的并发, 好像Spring的底层数据结构就是使用的ConcurrentHashMap
.
为了实现并发的高效率, ConcurrentHashMap
做了大量优化: 更为细化的分段锁(数组的每个容器的头节点作为锁), 无锁的读操作, 并发扩容等等.
此版的ConcurrentHashMap
与HashMap
思想差不多, 都是数组内存储链表(红黑树)的存储方式.
很多资料都在说Segment
, 这个概念是在早期版本中出现的概念(与concurrencyLevel
相关), 现在已经没有了, 现在ConcurrentHashMap
中的单独锁是锁住的头节点Node
. 请见此说明:
/**
* Stripped-down version of helper class used in previous version,
* declared for the sake of serialization compatibility
* 在先前版本中使用的精简版辅助类,为了序列化兼容性而声明
*/
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 类声明如下:
public class ConcurrentHashMap<K, V>
extends AbstractMap<K, V>
implements ConcurrentMap<K, V>, Serializable {
这里实现了 ConcurrentMap
接口, 字面意思: 并发Map. 我们先看一下这个接口, 有助于后面ConcurrentHashMap的理解.
ConcurrentMap接口说明
ConcurrentMap提供了类似于CAS的几个读写方法, 用于实现原子操作( 其实 ConcurrentHashMap 中的这几个方法就是用 Unsafe.CAS 实现的).
A {@link java.util.Map} providing additional atomic
{@code putIfAbsent}, {@code remove}, and {@code replace} methods.
<p>Memory consistency effects: As with other concurrent
collections, actions in a thread prior to placing an object into a
{@code ConcurrentMap} as a key or value
<a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
actions subsequent to the access or removal of that object from
the {@code ConcurrentMap} in another thread.
ConcurrentMap是一种额外提供原子性的putIfAbsent/remove/replace方法的Map.
内存一致性原则: 像其他并发集合一样, 较先发生在某线程中的写入操作 happen-before 在
另一个线程中较后进行的读取操作.
注: happen-before 讲解请见我的另一篇转载博客
https://blog.csdn.net/j550341130/article/details/80774359
下面看下每个方法, 方法注释里给予了类似逻辑的代码解释, 非常清晰, 所以就不再贴文字说明了.
putIfAbsent 方法
/**
* if (!map.containsKey(key))
* return map.put(key, value);
* else
* return map.get(key);}
*/
V putIfAbsent(K key, V value);
remove 方法
/**
* if (map.containsKey(key) && map.get(key).equals(value)) {
* map.remove(key);
* return true;
* } else {
* return false;
* }
*/
boolean remove(Object key, Object value);
replace 方法
/**
* if (map.containsKey(key) && map.get(key).equals(oldValue)) {
* map.put(key, newValue);
* return true;
* } else
* return false;}
*/
boolean replace(K key, V oldValue, V newValue);
/**
* if (map.containsKey(key)) {
* return map.put(key, value);
* } else {
* return null;
* }
*/
V replace(K key, V value);
ConcurrentHashMap 类注释
介绍了 ConcurrentHashMap 特性.
A hash table supporting full concurrency of retrievals and
high expected concurrency for updates. This class obeys the
same functional specification as {@link java.util.Hashtable}, and
includes versions of methods corresponding to each method of
{@code Hashtable}. However, even though all operations are
thread-safe, retrieval operations do <em>not</em> entail locking,
and there is <em>not</em> any support for locking the entire table
in a way that prevents all access. This class is fully
interoperable with {@code Hashtable} in programs that rely on its
thread safety but not on its synchronization details.
一种支持检索完全并发/更新高期望并发性的hash表. 此类遵循与HashTable相一致的
功能规范, 并且包含了与HashTable内每一个方法相对应的不同版本的方法. 不过, 即
使所有的操作都是线程安全的, 检索操作也不会强制锁定, 并且也没有任何支持会锁住
整个表来阻止访问. 此类在只要求线程安全不要求同步细节实现的场景下完全可以与
Hashtable通用.
<p>Retrieval operations (including {@code get}) generally do not
block, so may overlap with update operations (including {@code put}
and {@code remove}). Retrievals reflect the results of the most
recently <em>completed</em> update operations holding upon their
onset. (More formally, an update operation for a given key bears a
<em>happens-before</em> relation with any (non-null) retrieval for
that key reporting the updated value.) For aggregate operations
such as {@code putAll} and {@code clear}, concurrent retrievals may
reflect insertion or removal of only some entries. Similarly,
Iterators and Enumerations return elements reflecting the state of
the hash table at some point at or since the creation of the
iterator/enumeration.
They do <em>not</em> throw {@link
ConcurrentModificationException}. However, iterators are designed
to be used by only one thread at a time. Bear in mind that the
results of aggregate status methods including {@code size}, {@code
isEmpty}, and {@code containsValue} are typically useful only when
a map is not undergoing concurrent updates in other threads.
Otherwise the results of these methods reflect transient states
that may be adequate for monitoring or estimation purposes, but not
for program control.
get等检索操作通常不会锁住, 所以有可能与put/remove等更新操作同时发生. 检索操作
只会反映最近已经执行完毕的更新操作后的值(更正式的说法是, 一个key的更新操作会
happen-before于这个对应value的key的任何检索操作). 对于像putAll.clear之类的
聚合操作, 并发的检索结果可能只能反映其中的部分插入和删除. 同样, 迭代器和枚举器
返回的元素也会反映自身创建时的Hash表的状态.
迭代器和枚举器不会抛出ConcurrentModificationException异常. 迭代器被设计用于
单线程下使用. 注意, 包括size()/isEmpty()/containsValue()等聚合状态的方法返回
结果只有当map没有在其他线程被并发修改时才是有用的. 除非这些方法的结果只用于监测和
估计目的, 不用于程序控制.
<p>The table is dynamically expanded when there are too many
collisions (i.e., keys that have distinct hash codes but fall into
the same slot modulo the table size), with the expected average
effect of maintaining roughly two bins per mapping (corresponding
to a 0.75 load factor threshold for resizing). There may be much
variance around this average as mappings are add