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

来源:互联网 发布:图片剪切软件下载 编辑:程序博客网 时间:2024/06/07 03:14

在多线程环境下,非线程安全的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());    }}

0 0
原创粉丝点击