HashMap底层详解-003-resize、并发下的安全问题

HashMap底层详解-003-resize、并发下的安全问题_第1张图片
微信公众号:JavaWeb架构师

HashMap底层详解-003-resize、并发下的安全问题_第2张图片
微信公众号:JavaWeb架构师

HashMap底层详解-003-resize、并发下的安全问题_第3张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第4张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第5张图片
微信公众号:JavaWeb架构师
  • HashMap扩容的时机是: 当前HashMap中存放的元素个数 > HashMap的当前总长度 * loadFactor(loadFactor默认值是0.75) 时进行扩容,扩容之后的长度是当前长度的2倍(比如现在存放了 16 * 0.75 = 12个,再存放第十三个元素的时候,HashMap就会自动的扩容,扩展成 2 * 16 = 32个 )。
    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted

    int s = m.size();
    float ft = ((float)s / loadFactor) + 1.0F;
  • 当扩容之后,需要重新计算元素的位置,这是一个非常耗时的操作,所以我们最好能预设长度,比如我们有1000个元素,那么 new HashMap(1000)? 不!前面我们说过了,应该设置成2的幂,所以 new HashMap(1024)? 其实我们设置成1000也会被调整成1024的,但是这样就可以了吗?不!当元素超过 1024*0.75 = 768个的时候会进行自动扩容,所以我们应该是new HashMap(2048)。
HashMap底层详解-003-resize、并发下的安全问题_第6张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第7张图片
微信公众号:JavaWeb架构师

1.新建Entry,长度是原长度的两倍

2.rehash,遍历原有的数组,把那些元素重新映射到新的数组上(参考咱们前面的公式,length变了,结果自然就变了)。

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    // 遍历数组
    for (Entry e : table) {
        // 遍历数组中的链表
        while(null != e) {
            Entry next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            // 头插法
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}
HashMap底层详解-003-resize、并发下的安全问题_第8张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第9张图片
微信公众号:JavaWeb架构师
  1. 现在有一个HashMap如下,如果再插入一个元素就会进行扩容


    HashMap底层详解-003-resize、并发下的安全问题_第10张图片
    微信公众号:JavaWeb架构师

2.现在有线程A、B同时执行对这个HashMap的元素插入操作


HashMap底层详解-003-resize、并发下的安全问题_第11张图片
微信公众号:JavaWeb架构师

3.完成插入之后,A、B线程都要对这个HashMap进行扩容


HashMap底层详解-003-resize、并发下的安全问题_第12张图片
微信公众号:JavaWeb架构师

4.首先线程A执行


HashMap底层详解-003-resize、并发下的安全问题_第13张图片
微信公众号:JavaWeb架构师

当线程A对原HashMap遍历到了PHP位置的时候,刚刚执行完上面红框部分的代码,这时候:
e = PHP
next = Java
PHP.next = Java
Java.next = null

5.这时候线程A被挂起,线程B完成了所有的操作


HashMap底层详解-003-resize、并发下的安全问题_第14张图片
微信公众号:JavaWeb架构师

Java.next = PHP
PHP.next = null

6.线程A恢复执行-下标值


HashMap底层详解-003-resize、并发下的安全问题_第15张图片
微信公众号:JavaWeb架构师

i的值和线程B中PHP链表的下标一样(参数都是一样,结果就是一样的)

7.线程A恢复执行-PHP第一次
当前状态:
e = PHP
next = Java
Java.next = PHP
PHP.next = null


HashMap底层详解-003-resize、并发下的安全问题_第16张图片
微信公众号:JavaWeb架构师

这时候PHP被放到了线程A的新HashMap中


HashMap底层详解-003-resize、并发下的安全问题_第17张图片
微信公众号:JavaWeb架构师

现在:
e = Java
next = Java
Java.next = PHP
PHP.next = null

8.线程A恢复执行-Java第一次


HashMap底层详解-003-resize、并发下的安全问题_第18张图片
微信公众号:JavaWeb架构师

当前状态:
e = Java
next = PHP
Java.next = PHP
PHP.next = null

代码再次执行到了:


HashMap底层详解-003-resize、并发下的安全问题_第19张图片
微信公众号:JavaWeb架构师

这时候Java被放到了线程A的HashMap的i位置的表头

HashMap底层详解-003-resize、并发下的安全问题_第20张图片
微信公众号:JavaWeb架构师

现在:
e = PHP
next = PHP
Java.next = PHP
PHP.next = null

9.线程A恢复执行-PHP第二次(出现问题)


HashMap底层详解-003-resize、并发下的安全问题_第21张图片
微信公众号:JavaWeb架构师

当前状态:
e = PHP
next = null

HashMap底层详解-003-resize、并发下的安全问题_第22张图片
微信公众号:JavaWeb架构师

当前状态:
PHP.next = Java
Java.next = PHP
e = null


HashMap底层详解-003-resize、并发下的安全问题_第23张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第24张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第25张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第26张图片
微信公众号:JavaWeb架构师
HashMap底层详解-003-resize、并发下的安全问题_第27张图片
微信公众号:JavaWeb架构师


其它

  • 欢迎加入交流群:451826376

  • 更多信息:www.itcourse.top

完整教程PDF版本下载

你可能感兴趣的:(HashMap底层详解-003-resize、并发下的安全问题)