从源码看Android常用的数据结构 ( SDK23版本 ) ( 六, ConcurrentHashMap )

本文详细分析了Android SDK23版本中ConcurrentHashMap的实现原理,包括其并发优化策略、核心方法源码以及内部类和变量的作用。重点探讨了其无锁读操作、分段锁和并发扩容等特性,并与HashMap进行对比,揭示了ConcurrentHashMap在并发环境下的高效性能。
摘要由CSDN通过智能技术生成

此系列文章放在了我的专栏里, 欢迎查看
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做了大量优化: 更为细化的分段锁(数组的每个容器的头节点作为锁), 无锁的读操作, 并发扩容等等.

此版的ConcurrentHashMapHashMap思想差不多, 都是数组内存储链表(红黑树)的存储方式.
很多资料都在说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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值