【java并发编程】-03-并发容器

ConcurrentHashMap

实现原理

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

锁分段:将数据分成一段一段,每一段数据分配一把锁,当一个线程占用锁访问其中一个段数据的生时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap包含一个Segment数组,Segment是一种可重入锁,Segment结构类似于HashMap,是一种数组加链表的结构,一个Segment包含一个HashEntry数组。每个HashEntry是一个链表结构。当对HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁。

基本使用

get操作

get操作高效不加锁,是因为get方法里使用到的共享变量都定义成了volatile类型,包括统计当前Segment大小的count字段和存储值的HashEntry的value。根据java内存模型的happen before原则,保证对volatile字段读总能可见最后的写入。使得两个线程在同时写和读volatile变量,get操作也能拿到最新的值。这是valtile替换锁的经典应用场景。

 

put操作

1、判断是否需要扩容

    HashEntry数组是否超过容量

2、如何扩容

    只对HashEntry对应的Segment扩容到原容量的两倍,再将原数据再散列到新容器中。

 

size操作

要统计整个ConcurrentHashMap的元素大小,因为Segment的变量count是全局volatile类型,故可以相加得到统计值。但是再统计过程中如果不对put,remove,clean等修改操作加锁,可能会造成统计前后count值发生变化。如果加锁又影响效率。为此ConcurrentHashMap的做法是引入modCount变量。在put,remove,clean操作中都会修改modCount变量值。从而只需要在统计前后比较modCount值是否发生变化,如果连续尝试两次统计前后modCount都发生变化,则采用加锁的方式统计。

 

非阻塞队列ConcurrentLinkedQueue

实现原理

由head节点和tail节点组成,每个节点由节点元素item和指向下一个节点的next引用组成。

 

基本使用

入队列

通过tail定位到尾节点,然后通过cas操作将节点设置成尾节点的next节点,这里如果失败会重试。注意通过tail并不能一次定位到尾节点,原因如下。

添加元素到队尾,但并不是每次都更新tail到队尾节点,而是间隔hops次更新一次到尾结点,hops参数默认为1,这么做的原因在于设置tail的动作是cas操作,通过此方法能减少cas的操作次数,从而提高效率。

 

出队列

返回队列的第一个有元素的节点,并清空该节点对元素的引用。同tail一样,head节点也并不是每次都指向队列的第一个元素,同样是hops次数指向一次队列第一个有值元素,否则间隙情况下head指向的元素为空,其next引用指向的下一个元素才是真正有值需要返回的队列头元素。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值