JDK7之前HashMap集合形成环形链表
本文最后更新于5 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

在JDK7之前使用HashMap的时候,如果在并发场景下会导致集合在扩容的时候形成环形链表,出现线程安全问题

问题出现的原因

当数组的size>数组长度*一个因子(默认为0.75)时会触发数组扩容,其实也就是新建一个数组+链表的数据结构(大小为Old数组的两倍),然后将数据进行拷贝,引用指向新的数组,在拷贝数据时会调用transfer方法

void transfer(Entry[] newTable) {
    for (Entry<K,V> e : table) {
        while (null != e) {
            Entry<K,V> next = e.next; // (1) 关键点:先保存下一个节点
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];    // (2) 头插法:将e的next指向新表的表头
            newTable[i] = e;         // (3) 将e放到新表的表头
            e = next;                // (4) 处理下一个节点
        }
    }
}

该方法会使用头插法对链表进行拷贝,会将链表反序,正常单线程操作下,这个操作是没有问题的,但是在并发场景下会出现线程安全问题,如下:

假设数组的大小为10个桶,现在已经装了9个,线程一和线程二同时进行put操作,触发hashmap的扩容机制,调用transfer方法,假设线程一正常进行put

线程一将1号桶的链表倒序

此时线程二也要调用transfer方法,但是呢由于线程一的操作使得B.next == A,在用头插法对A节点进行插入时,会导致A.next指向B,从而形成环形链表,导致死循环,在遍历时占满CPU

该问题在JDK8之后改用尾插法后得到解决,如果使用尾插法便不会改变链表的顺序,即使线程一已经完成了操作,有序链表的顺序不变,故对线程二的操作没有影响。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇