Java并发容器和框架

目录

ConcurrentHashMap的实现原理与使用

为什么要使用ConcurrentHashMap

线程不安全的HashMap

效率低下的Hash Table

ConcurrentHashMap的锁分段技术可有效提升并发访问率

ConcurrentHashMap的结构

ConcurrentHashMap的初始化

初始化segments数组

初始化segmentShift和segmentMask

初始化每个segment

定位Segment

ConcurrentHashMap的操作

get操作

put操作

size橾作

JDK1.8版本的CurrentHashMap

1.8的节点

1.8的put

1.8的get

1.8的size

addCount()方法

1.7与1.8的区别

为什么1.8用Node替代Segment

为什么1.8用synchronized而不是ReentranLock

ConcurrentSkipListMap

CopyOnWriteArrayList

CopyOnWriteArrayList 是如何做到的?

CopyOnWriteArrayList 读取和写入源码简单分析

ConcurrentLinkedQueue

ConcurrentLinkedQueue的结构

入队列

入队列的过程

定位尾节点

设置入队节点为尾节点

HOPS的设计意图

出队列

Java中的阻塞队列

什么是阻塞队列

Java里的阻塞队列

ArrayBlockingQueue

LinkedBlockingQueue

PriorityBlockingQueue

DelayQueue

SynchronousQueue

LinkedTransferQueue

LinkedBlockingDeque

阻塞队列的实现原理

Fork/Join框架

什么是Fork/Join框架

工作窃取算法

Fork/Join框架的设计

使用Fork/Join框架

Fork/Join框架的异常处理

Fork/Join框架的实现原理

 ForkJoinTask的fork方法实现原理

ForkJoinTask的join方法实现原理


ConcurrentHashMap的实现原理与使用

为什么要使用ConcurrentHashMap

在并发编程中使用HashMap可能导致程序死循环。而使用线程安全的Hash Table效率又非 常低下,基于以上两个原因,便有了ConcurrentHashMap的登场机会。

线程不安全的HashMap

在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%, 所 以在并发情况下不能使用HashMap。例如,执行以下代码会引起死循环。

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

效率低下的Hash Table

Hash Table容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下Hash Table 的效率非常低下。因为当一个线程访问Hash Table的同步方法,其他线程也访问Hash Table的同 步方法时,会进入阻塞或轮询状态。如线程l使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。

ConcurrentHashMap的锁分段技术可有效提升并发访问率

Hash Table容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问Hash Table的 线程都必须竞争同一把锁,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么 当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并 发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。

首先将数据分成一段一段地存 储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数 据也能被其他线程访问。

ConcurrentHashMap的结构

注意:本文描述的conurrenthashmap是jdk1.7版本的

通过ConcurrentHashMap的类图来分析ConcurrentHashMap的结构,如图所示。

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。

Segment是一种可重 入锁(ReentrantLock), 在ConcurrentHashMap里扮演锁的角色;

ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

HashEntry则用于存储键值对数 据。

一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种 数组和链表结构。一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元 素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时, 必须首先获得与它对应的Segment锁

ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。如下图是ConcurrentHashMap的内部结构图:

从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作。

第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部。

ConcurrentHashMap的初始化

会初始化一个Segment数组,容量为16,而每个Segment呢,都继承了ReentrantLock类,也就是说每个Segment类本身就是一个锁,之后Segment内部又有一个table数组,而每个table数组里的索引数据呢,又对应着一个Node链表.

ConcurrentHashMap初始化方法是通过initialCapacity、loadFactor和concurrencyLevel等几个 参数来初始化segment数组、段偏移量segmentShift, 段掩码segmentMask和每个segment里的 HashEntry数组来实现的。

ConcurrentHashMap的构造函数有3个参数,initialCapacity是整个map的容量,loadFactor是负载,concurrencyLevel是预估的并发线程数,决定了segement数组的长度(大于等于它的2的n次方=ssize)。

每个segement里的HashEntry数组长度可以理解为initialCapacity除以ssize 的倍数C, 如果c大于1, 就会取大于等于c的2的N次方值

初始化segments数组

让我们来看一下初始化segments数组的源代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值