【Java并发编程的艺术】Java并发容器和框架:ConcurrentHashMap

感觉ConcurrentHashMap也要到复习Java基础的时候,留坑

1.为什么要用ConcurrentHashMap

1.1 线程不安全的HashMap

HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry。

1.2 效率低下的HashTable

HashTable容器在所有方法加synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。

1.3 ConcurrentHashMap

锁分段技术可有效提升并发访问率。
首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

2.结构

Segment是一种可重入锁,在ConcurrentHashMap中扮演锁的角色。HashEntry储存键值对数据。

一个ConcurrentHashMap中包含一个Segment数组,一个Segment中包含一个HashEntry数组。

【Java并发编程的艺术】Java并发容器和框架:ConcurrentHashMap
【Java并发编程的艺术】Java并发容器和框架:ConcurrentHashMap

3.初始化

4.操作

4.1 get

get操作不加锁。
它将要使用的共享变量都设置成volatile类型。
定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量count和value,所以可以不用加锁。

4.2 put

由于put方法需要对共享变量写入操作,所以为了线程安全,在操作共享变量时必须加锁。

4.3 size

先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如果统计过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小。
如何判断统计时容器是否变化?使用modCount变量,在put、remove和clean方法里操作元素前都会将变量modCount进行加1那么在统计size前后比较modCount是否发生变化,从而得知容器的大小是否发生变化。