JDK1.7 HashMap在多线程环境下偶然造成InfiniteLoop导致程序宕机

JDK1.7 头插法倒追:

在多线程环境下,非线程安全的hashmap可能会造成的问题

package littlehow.map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * HashMapInfiniteLoop  hashmap死循环造成的cpu 100%
 *  有时候,会出现很奇怪的现象,那就是程序运行一段时间会出现宕机,重启之后就没事儿了
 *  具体什么原因也很难查找。
 *  在高并发环境下,因为hashmap的线程不安全性,可能会引起上述现象
 *  现在就看看,这种现象如何发生的
 *
 * @author littlehow
 * @time 2016-07-11 10:16
 */
public class HashMapInfiniteLoop {
    //初始长度为4的hashmap
    private Map<Integer, String> map = new HashMap<Integer, String>(4);
    //线程数量
    final int count = 200;
    final CountDownLatch countDownLatch = new CountDownLatch(count);
    @Before
    public void init() {
        //因为hash计算结果类似于mod,所以我们可以事先准备冲突的hash对
        //rehash的源码,可以看出当size==threshold也就是4的时候会进行resize,
        //从而重新分布元素
        /** if ((size >= threshold) && (null != table[bucketIndex])) {
                resize(2 * table.length);
                hash = (null != key) ? hash(key) : 0;
                bucketIndex = indexFor(hash, table.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;
                 }
            }
         */
        // 3 11 7 15 put后这三个都出现了hash冲突,所以结构为 3 -> 11 -> 7 -> 15 的链表
        //当再put19时,又出现冲突, 这时候就需要扩容。
        // 正常情况形成两个链表  11 -> 3 -> 19 和 15 -> 7
        //但是当执行线程以执行到如  Entry<K,V> next = e.next;获取到的next=11时线程被挂起;
        //线程二来执行成功,线程一继续,e=7,next=15,进行rehash,执行到
        //newTable[i] = e = 7;  e = next = 15,而线程二已经排好 15 -> 7,
        //很明显线程一形成的链表为7 -> 15,合并结果就是 15 -> 7 -> 15形成了环形链表
        //这时候get(23)就会一直查找下去,因为元素一直拥有next。从而导致 infinite loop
        //resize后大小为8
        map.put(3, "littlehow");
        map.put(11, "color wolf");
        map.put(7, "black dog");
        map.put(15, "blue blood");
    }

    @Test
    public void conflict() {
        /**
         * 想要出现Entry<K,V> next = e.next;之后挂起线程非常困难,在并发量超级大的情况下有可能会出现
         * 所以没有出现死循环是最可能的现象,生产环境往往是不可预测的
         * 在多线程环境下使用map这样结构的类有两点建议:
         * 1.使用ConcurrentHashMap
         * 2.如果确定map元素多少的情况,最好初始化map尽量大,避免rehash
         */
        for (int i = 0; i < count; i ++) {
            new Thread("thread-" + (i + 1)) {
                @Override
                public void run() {
                    map.put(19, "new person");
                    countDownLatch.countDown();
                }
            }.start();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @After
    public void over() {
        System.out.println("value=" + map.get(23) + " , size=" + map.size());
    }
}

JDK1.8以及之后尾插法已解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值