由jdk7hashMap不安全产生的疑问,请熟悉JMM的大佬解答

jdk8之前的扩容操作会在并发情况下会导致链表形成回路:
关键在于transfer 方法采用头插法将新值放入链表头部

void transfer (Entry[]newTable,boolean rehash){
    int newCapacity = newTable.length;
    for (Entry<K, V> e : table) {
        while (null != e) { // 遍历链表放入新表                
            Entry<K, V> next = e.next; //最严重的部分,先保留了next然后线程被挂起,另外一个线程正常结束            
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);                 // 第一次插入链表头为null,即e.next = null           
            e.next = newTable[i];
            newTable[i] = e; // 放入桶中链表头               
            e = next;        // 指向下一个           
        } // while        
    }
}

看了很多解释为(假设e1.next = e2,并且重新hash后还在同一个桶里面):
1.线程A拿到next = e1.next即已经拿到下一个节点e2后挂起。此时e = e1,next = e2
2.线程B对链表扩容成功此时e2在链头,e2.next = e1.
3.此时线程A继续执行,头插法会把e即e1插入链头,因为next = e2 赋值给e!=null,则while不退出,再次执行逻辑,此时e2.next = e1(线程B造成的结果)导致变量e=next=e.next即e2.next = e1,那么e再次指向e1,此时桶中e2在链头,e2.next = e1
4.现在e=e1,继续执行逻辑,因为next=e1.next =null,循环结束,但是会把e1放入链头,并且桶中e1.next = e2,在第三步中e2.next = e1,这样变成了闭环。

这里有个问题:黄色高亮位置,e2.next = e1(线程B造成的结果)导致变量e=next=e.next即e2.next = e1,那么e再次指向e1
1:线程B结束后是怎么把工作内存刷回到主内存的
2:线程A为什么读取的是主内存e2.next = e1而不是自已的工作内存e2.next =null?

经过一些可见性的实验发现

读取数据前,无论调用sleep方法还是System.out.println()方法,甚至是new一个数组,或这发送一次http请求,都i会造成cpu把主内存的数据刷新到工作内存,一个没有根据的说法是:

这些方法的一个共同点就是,这些方法耗时,但是CPU计算占用很少。不是在做内存分配,就是在做IO,虽然占用了CPU时间片,但是CPU比较闲。
  原来,通过JVM大神们每天丧(干)心(得)病(漂)狂(亮)的努力,JVM针对现在的硬件水平已经做了很大程度的优化,基本上很大程度的保障了工作内存和主内存的及时同步,相当于默认使用了volitale。但只是最大程度。在CPU资源一直被占用的时候,工作内存与主内存中间的同步,也就是变量的可见性就不会那么及时!

但是我还是希望有JMM大佬能解答这个疑问

你可能感兴趣的:(Java基础)