1.7版本concurrenthashmap保证并发可见性的疑惑

来源:9-12 相关结构图

慕沐3053333

2020-03-23

//segment的定义
static final class Segment<K,V> extends ReentrantLock implements Serializable {
	...忽略...
	transient volatile HashEntry<K,V>[] table;
	...忽略...
}
//concurrenthashmap改变segment中hashentry数组中某一下标处的hashentry对象
/**
     * Sets the ith element of given table, with volatile write
     * semantics. (See above about use of putOrderedObject.)
     */
    static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i,
                                       HashEntry<K,V> e) {
        UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e);
    }

在remove操作中,当移除链表头节点时,会执行这段代码if (pred == null) setEntryAt(tab, index, next);这个方法里面又会调用UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e);,从而改变hashentry数组对应下标的hashentry对象。
我查阅资料:UNSAFE.putOrderedObjectUNSAFE.putObjectVolatile的区别是后者能保证可见性,前者是不能的。那这样1.7版本concurrenthashmap是怎么保证可见性的呢?比如,线程1remove操作修改了table[i]的链表头节点,指向了原链表的第二个hashentry对象。线程2紧接着进行get操作,读取这个table[i]的链表头节点。在这种场景下怎么保证线程2读取到的是remove过后的链表头节点(即原链表的第二个hashentry节点)。
这里我查阅了很多资料和帖子我都找不到我想要的答案,突然怀疑了自己对JMM理解的知识体系。。。。恳请悟空老师务必详细解答一下我的疑问

写回答

1回答

悟空

2020-03-24

remove后会解锁,由此具备了可见性,你可以看一下我第一门实战课讲过happens-before原理。

1
3
慕沐3053333
回复
悟空
可是remove操作最终调用的UNSAFE.putOrderedObject并没有像UNSAFE.putObjectVolatile那样立刻把工作内存的值刷回共享内存呀。此时尽管调用getObjectVolatile把共享内存的值刷回到工作内存,但是共享内存的值并不是最新的呀。
2020-03-24
共3条回复

深度解密Java并发工具,精通JUC,成为并发多面手

JUC全方位讲解,构建并发工具类知识体系

1599 学习 · 573 问题

查看课程