HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你! - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
crossoverJie
V2EX    程序员

HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!

  •  
  •   crossoverJie
    crossoverJie 2018 年 7 月 23 日 6009 次点击
    这是一个创建于 2739 天前的主题,其中的信息可能已经有所发展或是发生改变。
    45 条回复    2019-03-30 00:38:38 +08:00
    AntiGameZ
        1
    AntiGameZ  
       2018 年 7 月 23 日   1
    赞一下,Github 的文档整理的很有用,面试帮了不少忙
    crossoverJie
        2
    crossoverJie  
    OP
       2018 年 7 月 23 日
    guoyuchuan
        3
    guoyuchuan  
       2018 年 7 月 23 日
    正在看 GitHub 里面的东西,得用只是来充实自己。
    guoyuchuan
        4
    guoyuchuan  
       2018 年 7 月 23 日
    你的博客加载好慢啊,得等一会才能进去
    nVoxel
        5
    nVoxel  
       2018 年 7 月 23 日
    主题是不是换过?印象还挺深的~
    crossoverJie
        6
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @nVoxel #5 主题没换 就是换了一个配置 不想折腾博客了 但总得有点新鲜感
    crossoverJie
        7
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @guoyuchuan #4 没办法 用的 cloudflare 的 DNS 很慢,迁移到国内又要备案
    lsmgeb89
        8
    lsmgeb89  
       2018 年 7 月 23 日
    只记得完全无锁的实现非常复杂
    amon
        9
    amon  
       2018 年 7 月 23 日
    哎?每当关注了一个公众号不久,就在 v 站也看到作者。
    幸会幸会!
    crossoverJie
        10
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @amon #9 哈哈 V 站的人都超有趣的 很喜欢这里。
    LostMoonkin
        11
    LostMoonkin  
       2018 年 7 月 23 日
    看着博客的背景总感觉我屏幕脏了 /doge
    burenshiwo
        12
    burenshiwo  
       2018 年 7 月 23 日
    阿里的学渣.....
    crossoverJie
        13
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @chenjian026 #12 那篇还有人记着呢
    orm
        14
    orm  
       2018 年 7 月 23 日
    博客访问很慢
    alamaya
        15
    alamaya  
       2018 年 7 月 23 日
    当时看的时候觉得最复杂的是扩容那一块,你这篇好象没有涉及
    crossoverJie
        16
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @alamaya #15 嗯 扩容那块很复杂,得单独拿一篇讲才行。
    crossoverJie
        17
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @orm #14 看来迁回国内得提上议程了。
    llxx510200
        18
    llxx510200  
       2018 年 7 月 23 日
    要是问我红黑树可咋整啊
    crossoverJie
        19
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @llxx510200 #18 这个我觉得能讲清楚为啥快,啥时候翻转这些就差不多了。具体实现确实很复杂
    liuzhen
        20
    liuzhen  
       2018 年 7 月 23 日
    楼主博客可以转载到 oschina 吗?会备注原地址
    beny2mor
        21
    beny2mor  
       2018 年 7 月 23 日
    mark 一下
    crossoverJie
        22
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @liuzhen #20 可以的。
    Antidictator
        23
    Antidictator  
       2018 年 7 月 23 日 via iPhone
    支持一下
    LuckCode
        24
    LuckCode  
       2018 年 7 月 23 日 via iPhone
    我去,从博客看到公众号,今天在 v 站看到了作者。
    缘分啊,py 交易一下。
    WildCat
        25
    WildCat  
       2018 年 7 月 23 日   1
    支持。最近用 pandoc 把你的 repo 转换成了 epub 在手机上看,受益良多
    crossoverJie
        26
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @LuckCode #24 哈哈 我这是广撒网啊 你也是哪个网都钻进来了
    crossoverJie
        27
    crossoverJie  
    OP
       2018 年 7 月 23 日
    zxq1002
        28
    zxq1002  
       2018 年 7 月 23 日 via Android
    打开好慢。。
    WildCat
        29
    WildCat  
       2018 年 7 月 23 日
    @crossoverJie 我目前转换的有问题,不过我先开个 issue 抛砖引玉吧
    crossoverJie
        30
    crossoverJie  
    OP
       2018 年 7 月 23 日
    @zxq1002 #28 确实挺慢的,准备转到国内了。
    shidapi
        31
    shidapi  
       2018 年 7 月 24 日
    打不开。。。
    crossoverJie
        32
    crossoverJie  
    OP
       2018 年 7 月 24 日
    @shiguiyou #31 多等等 卡。。
    mrrobot97
        33
    mrrobot97  
       2018 年 7 月 24 日
    safari 禁用了 Flash 打开网页后风扇开始怒吼..
    zpxshl
        34
    zpxshl  
       2018 年 7 月 24 日 via Android
    我来提(请教)个问题。
    1.8 版 concurrentHM,当使用 cas 插入,如何保证结果对其他线程可见。 volatile 不保证数组元素的可见性。
    crossoverJie
        35
    crossoverJie  
    OP
       2018 年 7 月 24 日
    @zpxshl #34

    在 put 判断当前位置为空用 CAS 写入之前调用的是:

    tabAt() 方法,它所依赖的是 Unsafe 包中的 getObjectVolatile() 可以保证可见性。
    zpxshl
        36
    zpxshl  
       2018 年 7 月 25 日 via Android
    @crossoverJie
    #35 getObjectVolatile ()只能保证自己读到的是最新值。
    putObjectVolatile 才能保证将值写回主内存。
    crossoverJie
        37
    crossoverJie  
    OP
       2018 年 7 月 25 日
    @zpxshl #36 嗯 你不是问的怎么保证可见性嘛,获取到的是最新值就不会有可见性的问题了。
    zpxshl
        38
    zpxshl  
       2018 年 7 月 25 日 via Android
    @crossoverJie 可见性...... 是一个变量修改其他线程都可以见到。 你获取到最新值,(为空时) cas 插入。 此时数组元素被修改,如何保证其他线程见到你的修改?
    crossoverJie
        39
    crossoverJie  
    OP
       2018 年 7 月 25 日
    @zpxshl #38 如何保证其他线程见到你的修改?

    其他线程在 tabAt()



    这里不就获取了到最新值了嘛?也就是获取到了其他线程的修改。

    get 的时候也是一样的:

    zpxshl
        40
    zpxshl  
       2018 年 7 月 25 日 via Android
    @crossoverJie 朋友。getObjectVolatile 只是获得主内存的数据。并没有强制将其他线程的工作内存数据刷新回主内存的功能。 也就是说 当你 cas 修改数组数据后,如果没有将数据缓存刷回主内存。其他线程用 getObjectVolatile 也读不到最新的值。
    crossoverJie
        41
    crossoverJie  
    OP
       2018 年 7 月 25 日
    @zpxshl #40 兄弟,我觉得这没有啥问题呀。



    用 CAS 的前提是,遍历数组的时候在 key 所映射的那个位置是空的,也就是没有其他线程使用的时候才会在 2 处利用 CAS 尝试写入一个 Node。

    也就是说写入成功的话是不会有线程竞争的,这样也就不会存在你说的可见性问题(如果这时有线程来做 get ,恰好也定位到这个位置,那在写入线程更新成功之前获取的肯定也是空)。

    如果写入失败,CAS 返回 false,这样就会重新从 1 处再次循环。

    这样如果当前位置有数据了那就会用 synchronized 来写,用 synchronized 的话也不会出现可见性问题。

    如果当前位置依然为空的话就会重复上面的步骤。
    zpxshl
        42
    zpxshl  
       2018 年 7 月 26 日 via Android
    @crossoverJie
    a 线程,遍历数组的时候在 key 所映射的那个位置是空的,cas 插入成功。
    b 线程,同样找到那个位置发现同样为空( cas 插入的数值对 b 线程不一定可见的),又 cas 插入,那不就出问题啦?
    crossoverJie
        43
    crossoverJie  
    OP
       2018 年 7 月 27 日
    @zpxshl #42

    如果 A 线程写入成功之后 B 线程就算是恰巧在 A 线程还未写入之前判断到是空进入了 CAS 的逻辑。

    由于最终调用的还是 casTabAt()



    底层调用的是 unsafe.compareAndSwapObject 方法,他的期望是当前值为 null ,才会写入一个新的 Node。

    由于该方法是线程安全的,此时当前值已经不为 null 了,所以必定会写入失败。

    还有一个是,虽然是用 volatile 修饰的数组,但是用的是 getObjectVolatile 和 putObjectVolatile,依然可以保证可见性。

    见:

    https://stackoverflow.com/questions/31534706/about-unsafe-getobjectvolatile-usage
    zpxshl
        44
    zpxshl  
       2018 年 7 月 27 日
    @crossoverJie 感谢!
    c4f36e5766583218
        45
    c4f36e5766583218  
       2019 年 3 月 30 日
    jdk7 先扩容再 put 值;
    ```
    void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
    resize(2 * table.length);
    hash = (null != key) ? hash(key) : 0;
    bucketIndex = indexFor(hash, table.length);
    }
    createEntry(hash, key, value, bucketIndex);
    }
    ```
    jdk8 先 put 值再扩容: ```java.util.HashMap#putVal(int, K, V, boolean, boolean)```

    为什么?这么改动?有什么区别吗?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5794 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 02:42 PVG 10:42 LAX 18:42 JFK 21:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86