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

HashMap扩容机制详解

微信公众号:JavaWeb架构师
微信公众号:JavaWeb架构师
微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师

微信公众号: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)。

微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师

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

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

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;
            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;
        }
    }
}

微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师

  1. 现在有一个HashMap如下,如果再插入一个元素就会进行扩容
    微信公众号:JavaWeb架构师

2.现在有线程A、B同时执行对这个HashMap的元素插入操作
微信公众号:JavaWeb架构师

3.完成插入之后,A、B线程都要对这个HashMap进行扩容
微信公众号:JavaWeb架构师

4.首先线程A执行
微信公众号:JavaWeb架构师
当线程A对原HashMap遍历到了PHP位置的时候,刚刚执行完上面红框部分的代码,这时候:
e = PHP
next = Java
PHP.next = Java
Java.next = null

5.这时候线程A被挂起,线程B完成了所有的操作
微信公众号:JavaWeb架构师
Java.next = PHP
PHP.next = null

6.线程A恢复执行-下标值
微信公众号:JavaWeb架构师
i的值和线程B中PHP链表的下标一样(参数都是一样,结果就是一样的)

7.线程A恢复执行-PHP第一次
当前状态:
e = PHP
next = Java
Java.next = PHP
PHP.next = null
微信公众号:JavaWeb架构师
这时候PHP被放到了线程A的新HashMap中
微信公众号:JavaWeb架构师

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

8.线程A恢复执行-Java第一次
微信公众号:JavaWeb架构师
当前状态:
e = Java
next = PHP
Java.next = PHP
PHP.next = null

代码再次执行到了:
微信公众号:JavaWeb架构师

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

微信公众号:JavaWeb架构师

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

9.线程A恢复执行-PHP第二次(出现问题)
微信公众号:JavaWeb架构师
当前状态:
e = PHP
next = null

微信公众号:JavaWeb架构师

当前状态:
PHP.next = Java
Java.next = PHP
e = null
微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师

微信公众号:JavaWeb架构师


其它

完整教程PDF版本下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值