• 明翼
    2018-07-04
    1.7
    put加锁
    通过分段加锁segment,一个hashmap里有若干个segment,每个segment里有若干个桶,桶里存放K-V形式的链表,put数据时通过key哈希得到该元素要添加到的segment,然后对segment进行加锁,然后在哈希,计算得到给元素要添加到的桶,然后遍历桶中的链表,替换或新增节点到桶中

    size
    分段计算两次,两次结果相同则返回,否则对所以段加锁重新计算


    1.8
    put CAS 加锁
    1.8中不依赖与segment加锁,segment数量与桶数量一致;
    首先判断容器是否为空,为空则进行初始化利用volatile的sizeCtl作为互斥手段,如果发现竞争性的初始化,就暂停在那里,等待条件恢复,否则利用CAS设置排他标志(U.compareAndSwapInt(this, SIZECTL, sc, -1));否则重试
    对key hash计算得到该key存放的桶位置,判断该桶是否为空,为空则利用CAS设置新节点
    否则使用synchronize加锁,遍历桶中数据,替换或新增加点到桶中
    最后判断是否需要转为红黑树,转换之前判断是否需要扩容

    size
    利用LongAdd累加计算
    展开
     1
     96
  • 徐金铎
    2018-05-26
    需要注意的一点是,1.8以后的锁的颗粒度,是加在链表头上的,这个是个思路上的突破。

    作者回复: 是的

     4
     66
  • 雷霹雳的爸爸
    2018-05-26
    今天这个纯粹知识盲点,纯赞,源码也得不停看
    
     28
  • Sean
    2018-05-28
    最近用ConcurrentHashMap的场景是,由于系统是一个公共服务,全程异步处理。最后一环节需要http rest主动响应接入系统,于是为了定制化需求,利用netty写了一版异步http clinet。其在缓存tcp链接时用到了。
    看到下面有一位朋友说起了自旋锁和偏向锁。
    自旋锁个人理解的是cas的一种应用方式。并发包中的原子类是典型的应用。
    偏向锁个人理解的是获取锁的优化。在ReentrantLock中用于实现已获取完锁的的线程重入问题。
    不知道理解的是否有误差。欢迎指正探讨。谢谢
    展开

    作者回复: 正确,互相交流
    偏向锁,侧重是低竞争场景的优化,去掉可能不必要的同步

    
     27
  • t
    2018-07-03
    对于我这种菜鸟来说,应该来一期讲讲volatile😭
    
     22
  • j.c.
    2018-05-26
    期待unsafe和cas的文章
    
     22
  • 虞飞
    2018-05-27
    老师在课程里讲到同步包装类比较低效,不太适合高并发的场景,那想请教一下老师,在list接口的实现类中。在高并发的场景下,选择哪种实现类比较好?因为ArrayList是线程不安全的,同步包装类又很低效,CopyonwriteArrayList又是以快照的形式来实现的,在频繁写入数据的时候,其实也很低效,那这个类型该怎么选择比较好?

    作者回复: 目前并发list好像就那一个,我觉得不必拘泥于list,不还有queue之类,看场景需要的真是list吗

    
     9
  • Kyle
    2018-05-28
    之前用JavaFX做一个客户端IM工具的时候,我将拉来的未被读取的用户聊天信息用ConcurrentHashMap存储(同时异步存储到Sqlite),Key存放用户id,Value放未读取的聊天消息列表。因为我考虑到存消息和读消息是由两个线程并发处理的,这两个线程共同操作一个ConcurrentHashMap。可能是我没处理好,最后直到我离职了还有消息重复、乱序的问题。请问我这种应用场景有什么问题吗?
    
     6
  • coder王
    2018-05-28
    您说的synchronized被改进很多很多了,那么在我们平常使用中,就用这个synchronized完成一些同步操作是不是OK?😁

    作者回复: 通常是的,前提是JDK版本需要新一点

     1
     6
  • 约书亚
    2018-05-26
    这期内容太难,分寸不好把握
    看8的concurenthashmap源码感觉挺困难,网上的博文帮助也不大,尤其是扩容这部分(似乎文章中没提)
    求问杨大有没有什么窍门,或者有什么启发性的paper或文章?
    可以泛化成,长期对lock free实现多个状态修改的问题比较困惑,希望得到启发

    作者回复: 本文尽量梳理了相对比较容易理解的部分;扩容细节我觉得是个加分项,不是每个人都会在乎那么深入;窍门,可以考虑画图辅助理解,我是比较笨的类型,除了死磕,不会太多窍门……

    
     5
  • Answer
    2018-07-03
    Unsafe?
    
     4
  • shawn
    2018-07-02
    老师,什么只有bin为空的时候才使用cas,其他地方用synchronized 呢?
    
     4
  • mongo
    2018-05-26
    请教老师:putVal方法中,什么情况下会进入else if ((fh=f.hash) == MOVED)分支?是进行扩容的时候吗?nextTable是做什么用的?

    作者回复: 我理解是的,判断是个ForwardingNode,resize正在进行;
    nexttable是扩容时的临时过渡

    
     4
  • mongo
    2018-05-26
    请教老师:putVal方法的第二个if分支,为什么要用tabAt?我的认识里直接数组下标寻址tab[i=(n-1) & hash]也是一个原子操作,不是吗?tabAt里面的getObjectVolatle()方法跟直接用数组下标tab[i=(n-1) & hash]寻址有什么区别?

    作者回复: 这个有volatile load语义

    
     4
  • Leiy
    2018-05-29
    我感觉jdk8就相当于把segment分段锁更细粒度了,每个数组元素就是原来一个segment,那并发度就由原来segment数变为数组长度?而且用到了cas乐观锁,所以能支持更高的并发,不知道我这种理解对吗?如果对的话,我就在想,为什么并发大神之前没想到这种,哈哈😄,恳请指正。谢谢

    作者回复: 基本正确,cas只用在部分场景;
    事后看容易啊,说比做容易,😄

    
     3
  • Xg huang
    2018-06-08
    这里有个地方想跟老师交流一下想法, 从文中"所以,ConcurrentHashMap 的实现是通过重试机制(RETRIES_BEFORE_LOCK,指定重试次数 2),来试图获得可靠值。如果没有监控到发生变化(通过对比 Segment.modCount),就直接返回,否则获取锁进行操作。" 可以看出, 在高并发的情况下, "size()" 方法只是返回"近似值", 而我的问题是: 既然只是一个近似值, 为啥要用这种"重试,分段锁" 的复杂做法去计算这个值? 直接在不加锁的情况下返回segment 的size 岂不是更简单? 我能理解jdk开发者想尽一切努力在高性能地返回最精确的数值, 但这个"精确" 度无法量化啊, 对于调用方来说,这个值依然是不可靠的啊. 所以, 在我看来,这种做法收益很小(可能是我也比较懒吧), 或者有些设计上的要点我没有领悟出来, 希望老师指点一下.

    作者回复: 这个是在代价可接受情况下,尽量准确,就像含金量90%和99.9%,99.999%,还是有区别的,虽然不是百分百

    
     2
  • Hesher
    2018-05-28
    并发包用的很少,这一节内容的前置知识比较多,对于使用经验少的人来说貌似是有点难了。问题很好,正好可以见识一下各种使用场景,不过留言大部分是针对内容的难点提问,而真正回答问题的还没有出现。

    作者回复: 后面并发部分会详细分析

    
     2
  • 行者
    2018-05-27
    老师麻烦讲讲自旋锁,偏向锁的特点和区别吧,一直不太清楚。

    作者回复: 好,后面有章节

    
     2
  • 日光倾城
    2019-08-25
    initTable里面的Thread.yield什么场景下会返回呢
     2
     1
  • QQ怪
    2019-03-28
    我记得concurrentHashMap的size方法是一个嵌套循环:
    1:遍历所有的segment;
    2:把所有segment元素累加起来;
    3:把所有segment的修改次数累加起来;
    4:判断当次segment修改次数是否大于上次的总修改次数,如果大于,则说明当次还有修改,重新统计,尝试一次,如果不是,说明没有修改,则结束;
    5:如果尝试次数超过阈值,则对每个segment加锁,重新统计,最后在重试4步骤,只到总修改次数大于上次修改次数,释放锁,然后统计结束。
    展开
    
     1
我们在线,来聊聊吧