`

HashMap-getEntry多线程阻塞分析

 
阅读更多
HashMap

本文记述一次hashMap引起阻塞问题


问题现象:
程序是将一个数据库里面的数据导入到另外一个数据库中,两边表结构不一样但业务是一致的(mysql->mssql)。由于每次都是全量循环校验导入,所以运行时间要2-3个小时。

运行过程中,发现在调用jdbcTemplate查询的过程中,经常发生阻塞。



问题分析:
一开始以为,是数据库连接池问题或网络较慢,一直在等待数据库连接或数据库服务器端任务阻塞,一直等待执行

光靠猜想是没有道理的,还是dump看下,


"Thread-1" prio=6 tid=0x000000000b365000 nid=0x15e8 runnable [0x000000000d2af000]
   java.lang.Thread.State: RUNNABLE
	at java.util.HashMap.getEntry(HashMap.java:446)
	at java.util.HashMap.containsKey(HashMap.java:434)
	at java.util.HashSet.contains(HashSet.java:201)

"Thread-0" prio=6 tid=0x000000000b364800 nid=0x714 runnable [0x000000000ca7e000]
   java.lang.Thread.State: RUNNABLE
	at java.util.HashMap.getEntry(HashMap.java:446)
	at java.util.HashMap.containsKey(HashMap.java:434)
	at java.util.HashSet.contains(HashSet.java:201)




    final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }


应该是在e = e.next一直循环,那为什么会形成环呢?

然后各种baidu,google,在多线程情况下,hashMap扩容的时候,可能生成环
    /**
     * Transfers all entries from current table to newTable.
     */
    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;
            }
        }
    }


扩容时,会将旧表里面的数据复制到新扩容的表中,该任务由transfer方法执行





引用

线程一,执行上述transfer代码:

  线程一:

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

这时,线程1线程栈中,e为3,next值为7


然后,线程2执行上述代码,最终将结点3,结点7放入新表中,即: 7和3在table下标为3中,7->3

在这个过程中,线程2修改了引用结点7,使其中的next字段指向了3(这就是引起闭环的主要原因)


接着线程一继续执行,当前e为3,next为7

第一步,将当前处理的结点3放入新表newTable(下标为3),然后将结点3的next指向null(因此newTable是处于线程栈中,是线程私有的,并不受线程2影响)

  这部处理完后,newTable下标为3的内容为:3(其next指向null)

第二步,处理结点7,这时当前结点e为7,而next由于线程2修改了其引用,所以next为3。

            这步处理完后,其newTable下标为3的内容为:7(指向3),3(指向null)


第三步,这步是引起闭环的最终原因,当前处理结点为3,next值为null

        这时使3指向newTable中的7,然后将3存入newTable

      这步处理完后,newTable下标为3的内容为:

    3(指向7),7(指向3)。


查找时,查找key为11的值,这时查找table下标为3的内容,先找3,不匹配,然后找7.也不对,这时查7指向的3.。。。。

死循环





参考:
【java基础 12】HashMap中是如何形成环形链表的?
http://blog.csdn.net/hhx0626/article/details/54024222

HashMap并发情况下,导致闭环链
http://blog.csdn.net/zhaozhenzuo/article/details/18800063


并发环境下HashMap引起full gc排查
http://blog.lichengwu.cn/java/2015/04/06/case-of-hashmap-in-concurrency/

多线程下HashMap的死循环问题
http://www.cnblogs.com/ITtangtang/p/3966467.html
分享到:
评论

相关推荐

    20-集合框架020-HashMap-1080P 高清-AVC20

    1. **非同步**:HashMap不是线程安全的,这意味着在多线程环境中,如果不采取同步措施,可能会出现数据不一致的情况。如果需要线程安全的Map,可以考虑使用ConcurrentHashMap。 2. **允许null键和null值**:HashMap...

    c_hashmap-master_hashmap.keys_C-C++_establishgkb_TheKeys_

    在分析“c_hashmap-master”的源代码时,我们需要特别关注这些关键部分,理解它们是如何协同工作的。同时,由于这是个C语言实现,所以内存管理(如malloc和free)和错误处理是需要特别关注的方面。 总结来说,"c_...

    hashmap-thread-test:测试 Java HashMap 是否是线程安全的

    然而,`HashMap`并非线程安全,这意味着在多线程环境中直接使用`HashMap`可能会导致数据不一致、并发问题,甚至程序崩溃。本项目“hashmap-thread-test”显然是为了测试和展示这一特性。 ### Java HashMap 的特性 ...

    HashMap-master.zip_hash

    标题"HashMap-master.zip_hash"可能指的是一个关于HashMap实现或优化的项目,其中包含了哈希函数的设计和分析。 哈希表的核心思想是通过哈希函数将键(Key)映射到数组的特定位置,使得我们能够以近乎常数的时间...

    HashMap-面试必过

    1.说一下 HashMap 的实现原理? 2.HashMap在JDK1.7和JDK1.8中有哪些不同?HashMap的底层实现? 3.HashMap的put方法的具体流程? 4.HashMap的扩容操作是怎么实现的? 5.HashMap是怎么解决哈希冲突的? 6.什么是哈希?...

    grunt-hashmap-ext

    同时,这也是一种优化,因为避免了因多次声明变量导致的额外内存开销。 值得注意的是,尽管"grunt-hashmap-ext"提供了便利,但它并不适用于所有场景。在某些情况下,例如在需要长期存储或跨函数共享数据时,传统的...

    HashMap-1.7的元素重分配原理运行图.pptx

    Java、hashMap

    关于如何解决HashMap线程安全问题的介绍

    4. 避免在多线程环境中直接使用HashMap:如果你确定不需要在多线程环境下共享HashMap,那么可以考虑局部变量的方式,只在单个线程中使用HashMap,这样就无需担心线程安全问题。 总结起来,理解HashMap的线程不安全...

    HashMap-hash原理

    官方

    HashMap源码分析系列-第四弹:HashMap多线程解决方案.docx

    HashMap源码分析系列-第四弹:HashMap多线程解决方案.docx

    后端开发-06-线程执行带有参数的任务.ev4.rar

    - **多线程优点**:提高系统资源利用率,提高程序响应速度,实现并发执行,提升整体性能。 2. **线程执行任务** - **创建线程**:在Java等语言中,可以通过继承Thread类或者实现Runnable接口来创建线程。 - **...

    Generic-HashMap-Java:Java的通用HashMap

    - 不是线程安全的,如果在多线程环境下使用,需要手动同步或使用ConcurrentHashMap。 - 默认负载因子为0.75,当HashMap的容量达到这个比例时,会自动进行扩容操作,以保持良好的性能。 5. **项目"Generic-HashMap...

    Go-Golang无锁线程安全的HashMap为最快的读取访问进行了优化

    在多线程环境中,为了保证数据的一致性和正确性,通常需要引入锁机制,但这会增加额外的开销,降低并发性能。Go语言提供了通道(Channel)和原子操作(Atomic Operations)等原生并发工具,使得无锁实现成为可能。 ...

    java的hashMap多线程并发情况下扩容产生的死锁问题解决.docx

    这就有可能导致A线程和B线程同时对一个数组扩容,A线程扩容后替换掉老数组,这时B线程使用的数组实际上是A线程扩容后的数组,就会产生线程安全问题。 死锁原因 比如,当前集合数组长度为2,已经有两个元素被放在了...

    java-java面试题库整理-基础-JVM-线程并发-框架等.zip

    - 集合框架:掌握ArrayList、LinkedList、HashMap、HashSet等容器的使用及其原理。 - 泛型:了解泛型的作用,如何使用泛型类和泛型方法。 - I/O流:理解流的概念,学习字节流和字符流,以及缓冲流的使用。 2. **...

    01-HashMap底层数据结构分析.mp4

    01-HashMap底层数据结构分析.mp4

    读取excel文件数据,封装成hashmap-附件资源

    读取excel文件数据,封装成hashmap-附件资源

    HashMap 概述 精讲 .md

    看完这篇 HashMap,和面试官扯皮就没问题了 - HashMap 概述 - HashMap 和 HashTable 的区别 - 相同点 - 不同点 - HashMap 和 HashSet 的区别 - HashMap 底层结构 - AbstractMap 类 - Map 接口 - 重要内部类...

    Java多线程应用练习源代码及相关说明资料

    - **线程状态**:线程有新建、就绪、运行、阻塞和死亡五种状态,理解这些状态有助于分析和调试多线程问题。 2. **线程同步与通信** - **同步机制**:包括synchronized关键字、volatile变量、Lock接口(如...

Global site tag (gtag.js) - Google Analytics