分析多线程并发写HashMap线程被hang住的原因

计算机考研考博经典考题汇总(一次刷新世界观-我相信VIP总是有原因的) 操作系统 操作系统的特点? – 共享:资源可被多个并发执行的进程使用 – 并发:可以在同一时间间隔处理多个进程,需要硬件支持 – 虚拟:将物理实体映射成为多个虚拟设备 – 异步:进程执行走走停停,每次进程执行速度可能不同,但OS需保证进程每次执行结果相同 进程的三个组成部分? 程序段、数据段、PCB(Process Control Block) 并发与并行区别? 并发:同一间隔 并行:同一时刻 进程切换的过程? 保持处理机上下文 -> 更新PCB -> 把PCB移入相应队列(就绪、阻塞) -&g 阅读详情
public class TestLock {
  private final HashMap map = new HashMap();
  public TestLock() {
    final Thread t1 = new Thread() {
      @Override
      public void run() {
        for(int i=0; i<500000; i++) {
          map.put(new Integer(i), i);
        }
        System.out.println("t1 over");
      }
    };
    final Thread t2 = new Thread() {
      @Override
      public void run() {
        for(int i=0; i<500000; i++) {
          map.put(new Integer(i), i);
        }
        System.out.println("t2 over");
      }
    };
    t1.start();
    t2.start();
  }
 
  public static void main(final String[] args) {
    new TestLock();
  }
}
就是启了两个线程,不断的往一个非线程安全的HashMap中put内容,put的内容很简单,key和value都是从0自增的整数(这个put的内容做的并不好,以致于后来干扰了我分析问题的思路)。对HashMap做并发写操作,我原以为只不过会产生脏数据的情况,但反复运行这个程序,会出现线程t1、t2被hang住的情况,多数情况下是一个线程被hang住另一个成功结束,偶尔会两个线程都被hang住。说到这里,你如果觉得不好好学习ConcurrentHashMap而在这瞎折腾就手下留情跳过吧。
好吧,分析下HashMap的put函数 源码 看看问题出在哪,这里就罗列出相关代码(jdk1.6):
 www.2cto.com
public V put(final K key, final V value) {
    if (key == null) {
      return putForNullKey(value);
    }
    final int hash = hash(key.hashCode());
    final int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {//@标记1
      Object k;
      if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
        final V oldValue = e.value;
        e.value = value;
        e.recordAccess(this);
        return oldValue;
      }
    }
 
    modCount++;
    addEntry(hash, key, value, i);
    return null;
  }
 
void addEntry(final int hash, final K key, final V value, final int bucketIndex) {
    final Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
    if (size++ >= threshold) {
      resize(2 * table.length);
    }
  }
 
void resize(final int newCapacity) {
    final Entry[] oldTable = table;
    final int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
      threshold = Integer.MAX_VALUE;
      return;
    }
 
    final Entry[] newTable = new Entry[newCapacity];
    transfer(newTable);
    table = newTable;
    threshold = (int)(newCapacity * loadFactor);
  }
 
void transfer(final Entry[] newTable) {
    final Entry[] src = table;
    final int newCapacity = newTable.length;
    final long time1 = System.currentTimeMillis();
    for (int j = 0; j < src.length; j++) {
      Entry<K,V> e = src[j];
      if (e != null) {
        src[j] = null;
        int k=0;//@标记2
        do {
          final Entry<K,V> next = e.next;
          final int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
          if (k++ > 1000) {//@标记3
            System.out.println(Thread.currentThread().getName()+
                ",e==next:"+(e==e.next)+",e==next.next:"+(e==e.next.next)+
                ",e:"+e+",next:"+e.next+",eq:"+e.equals(e.next));
            try {
              Thread.sleep(2000);
            } catch (final Exception e2) {
            }
 
          }
        } while (e != null);
      }
    }
  }
通过jconsole(或者thread dump),可以看到线程停在了transfer方法的while循环处。这个transfer方法的作用是,当Map中元素数超过阈值需要resize时,它负责把原Map中的元素映射到新Map中。我修改了HashMap,加上了@标记2和@标记3的代码片断,以打印出死循环时的状态,结果死循环线程总是出现类似这样的输出:“Thread-1,e==next:false,e==next.next:true,e:108928=108928,next:108928=108928,eq:true”。
这个输出表明:
1)这个Entry链中的两个Entry之间的关系是:e=e.next.next,造成死循环。
2)e.equals(e.next),但e!=e.next。因为测试例子中两个线程put的内容一样,并发时可能同一个key被保存了多个value,这种错误是在addEntry函数产生的,但这和线程死循环没有关系。
 
接下来就分析transfer中那个while循环了。先所说这个循环正常的功能:src[j]保存的是映射成同一个hash值的多个Entry的链表,这个src[j]可能为null,可能只有一个Entry,也可能由多个Entry链接起来。假设是多个Entry,原来的链是(src[j]=a)->b(也就是src[j]=a,a.next=b,b.next=null),经过while处理后得到了(newTable[i]=b)->a。也就是说,把链表的next关系反向了。
 
再看看这个while中可能在多线程情况下引起问题的语句。针对两个线程t1和t2,这里它们可能的产生问题的执行序列做些个人分析:
 
1)假设同一个Entry列表[e->f->...],t1先到,t2后到并都走到while中。t1执行“e.next = newTable[i];newTable[i] = e;”这使得e.next=null(初始的newTable[i]为null),newTable[i]指向了e。这时t2执行了“e.next = newTable[i];newTable[i] = e;”,这使得e.next=e,e死循环了。因为循环开始处的“final Entrynext = e.next;”,尽管e自己死循环了,在最后的“e = next;”后,两个线程都会跳过e继续执行下去。
 
2)在while中逐个遍历Entry链表中的Entry而把next关系反向时,newTable[i]成为了被交换的引用,可疑的语句在于“e.next = newTable[i];”。假设链表e->f->g被t1处理成eg,造成了死循环。所以,理论上来说,死循环的Entry个数可能很多。尽管产生了死循环,但是t1执行到了死循环的右边,所以是会继续执行下去的,而t2如果执行“final Entrynext = e.next;”的next为null,则也会继续执行下去,否则就进入了死循环。
 
3)似乎情况会更复杂,因为即便线程跳出了死循环,它下一次做resize进入transfer时,有可能因为之前的死循环Entry链表而被hang住(似乎是一定会被hang住)。也有可能,在put检查Entry链表时(@标记1),因为Entry链表的死循环而被hang住。也似乎有可能,活着的线程和死循环的线程同时执行在while里后,两个线程都能活着出去。所以,可能两个线程平安退出,可能一个线程hang在transfer中,可能两个线程都被hang住而又不一定在一个地方。
 
4)我反复的测试,出现一个线程被hang住的情况最多,都是e=e.next.next造成的,这主要就是例子put两份增量数据造成的。我如果去掉@标记3的输出,有时也能复现两个线程都被hang住的情况,但加上后就很难复现出来。我又把put的数据改了下,比如让两个线程put范围不同的数据,就能复现出e=e.next,两个线程都被hang住的情况。
 

上面罗哩罗嗦了很多,一开始我简单的分析后觉得似乎明白了怎么回事,可现在仔细琢磨后似乎又不明白了许多。有一个细节是,每次死循环的key的大小也是有据可循的,我就不打哈了。感觉,如果样本多些,可能出现问题的原因点会很多,也会更复杂,我姑且不再蛋疼下去。至于有人提到ConcurrentHashMap也有这个问题,我觉得不大可能,因为它的put操作是加锁的,如果有这个问题就不叫线程安全的Map了。

本文转载于[http://www.2cto.com/kf/201206/135697.html]

Linux: hang: 线程太多,导致的一个例子 不过还是怀疑,是因为线程太多,导致资源占用比较严重,从而导致一个hang的假象:ssh登陆不上去,console没有相应。今天遇到了一个Linux系统hang住的情况,从vmcore里看bt,没有看到明显的crash、lockup等信息;而且从CPU的统计数据上看,当问题出现时,CPU一直处于高位。而且从vmcore里也不能看具体的当时CPU事情情况。 阅读详情

相关推荐

FFTW多核优化避坑指南:从交叉编译到ELF2板载OpenMP部署全流程

本文详细介绍了在ELF2开发板(RK3588平台)上为FFTW库配置OpenMP多核并行的完整流程与避坑指南。内容涵盖从交叉编译、库依赖处理到板载部署的实战步骤,并深入分析了RK3588的big.LITTLE架构对线程调优的影响,帮助开发者在嵌入式环境中高效利用多核性能。

2a4s6d8f0g的博客 272

一次诡异的垃圾回收耗时问题排查实践

之前的博文中说过最近在查一个问题,花费了近两个星期,问题算是有了一个小结,是时候总结一下了。排查过程走了很多弯路,由于眼界和知识储备问题,也进入了一些思维误区,希望此问题能以后再查询此类问题时能有所警示和参考;而且很多排查方法和思路都来自于部门 leader 和 组里大神给的提示和启发,总结一下也能对这些知识有更深的理解。这个问题出现在典型的高并发场景下,现象是某个接口会偶尔超时,查了几个 cas...

cpongo011702 689

卫星测试科普(三):卫星载荷的核心测试设备探秘

总的说来,卫星芯片与元器件,部组件与单机,整机与子系统,系统与整星的设计、定型、制造、验证、总装、发射、在轨等环节都离不开各种测试场景,对于这些卫星测试场景而言,“射频三大件”与高速数字示波器是构成强大测试系统的基础。卫星通信信号生成:覆盖Ka、Q等波段,具备扫频、脉冲、数字调制、噪声信号、雷达信号、数据链信号、OFDM 等多种信号库,用于测试和验证卫星电子设备的电气性能、频率响应、抗干扰能力等。卫星的通信需要高效、快速地传输大量数据,高速信号的质量至关重要,如果卫星信号不佳,卫星就会出现故障或者隐患。

spwbjss的博客 2319

58 应用服务 hang 住, 导致服务 503 Service Unavailable

这是之前 我们测试环境出现的一个问题一个项目, 代码调整了之后, 发布到测试环境 之后, 几分钟之后 整个系统访问这个服务 出现了 "503 Service Unavailable", 然后 当时的处理方式为 临时重启服务但是过了一会儿之后 同样的问题还是会出现, 导致 前端服务调试不了这个我在调试的时候, 没有怎么查看日志, 因为一些信息已经还是比较明显了, 这里会贴出 具体的日志假设这个服务为 ServiceA。

970655147的专栏 1985

Transformer在量化交易中的应用:从时序预测到策略生成

Transformer模型凭借其强大的自注意力机制,在自然语言处理领域取得了革命性突破。其核心原理是通过并行计算和动态权重分配,有效捕捉序列数据中的长距离依赖关系。这一特性使其在金融时间序列分析中展现出巨大技术价值,能够处理市场数据中的复杂非线性模式和长期关联。在量化交易场景下,Transformer可将价格、成交量等连续数据通过离散化分桶处理,转化为类似文本的序列,进而进行预测或直接生成交易信号。本文以QuantGPT项目为例,探讨了如何将这一前沿AI架构迁移应用于金融领域,涵盖了从数据作为“语言”的编码

weixin_26757949的博客 366

unity转微信小游戏:UI大小设置

unity转微信小游戏:UI大小设置

会潜水的小火龙的博客 3273

JVM致命错误日志详解

崩溃时正在通过JNI方式调用SetByteArrayRegion这个方法进行数组处理,通过堆栈信息可以看到是在调用RemoteTCPConnection.receive()时报的错,而这个类是我们引用的MQ中的方法,后续需要对相关的代码进行排查,确定使用的版本是否正常,相关代码是否存在问题。

匠心工坊 5082

VMware player桥接模式不能联网的解决方法

VMware虚拟机下主要使用两种网络连接方式:桥接模式、NAT模式。 桥接模式:查一下,虚拟机独立IP。 NAT模式:与电脑共享IP、需要映射应用服务的端口。 我的需求需要独立IP,于是使用桥接。 网络连接失败。 个人主机系统的网络设置: 设置 - 网络和 Internet - 更改适配器设置 给虚拟机设置网络适配器 只保留,点击确定,确定。 重新运行虚拟机。 ...

Mark_md的博客 9450

2021-07-30 JVM hang之写pipe阻塞

发展方向越来越明晰了呢: 掌握一个特定的开发领域,熟练且精通。什么叫熟练,就是在这个领域下,能扶起来一个应用,达成你想要的功能 但向深度,往哪里深呢,我还是更喜欢OS,弄清OS,几乎没问题能挡住你,为什么不向一个框架深入呢?以我目前的认知,要熟悉一个框架就是看说明书 另一方向,需要学习大量的开源代码来掌握编程模式,编程套路 更深一些,即算法,它可以有理论依据的、巧妙地去解决一个复杂问题,而不是去凑(数学之美这本书告诉我的),比如一个有限状态机的实现过程,比如看看Spring是如何使用IoC来实现对象组合的(

Jimmy的博客 402

Pcileech(DMA)开源项目简述

希望大DMA家能正确认识FPGA,正确使用DMA技术,对于市场上各类伪装成花里胡哨设备的FPGA板卡它的本质是不会变的,不要上当受骗。

2301_79773847的博客 5963

简易电梯控制系统设计(两层楼)(

(电子科技大学 数字系统实验二 结课验收项目 ) 题目要求:

m0_48801027的博客 1万+

java hashmap 异步_基于HashMap多线程并发问题分析

问题描述最近生产系统,某一台业务服务器偶发出现CPU执行90%以上。更严重者导致系统hang掉,事先未找到代码原因,拉取java core里堆栈信息后,发现发现有几个线程的状态都是Runnable,程序定位在InventoryReportServiceImpl 服务类上,其他的线程都处于Waiting状态代码定位:找到该代码位置之后,发现此处代码的业务逻辑为:根据商品SKU去查询对应的商品主数据信...

weixin_34060561的博客 516

ESD防护设计宝典(十一):CE(传导发射)超标问题排查与解决

(Conducted Emission, CE),亦称为传导骚扰,是电子电气设备或系统内部的电压、电流通过信号线、电源线或地线向外传输,对其他电子电气设备或系统造成电磁干扰的物理现象。其。

m0_56451176的博客 857

Java之美[从菜鸟到高手演变]之HashMap、HashTable

转自:blog.youkuaiyun.com/zhangerqing/article/details/8193118A Java集合类是个非常重要的知识点,HashMap、HashTable、ConcurrentHashMap等算是集合类中的重点,可谓“重中之重”,首先来看个问题,如面试官问你:HashMap和HashTable有什么区别,一个比较简单的回答是: 1、HashMap是非线程...

delengyao1368的博客 121

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

小心LinkedHashMap的get()方法

这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应put()方法,而读锁对应get()方法,期望通过读写锁来实现并发get()。    代码实现如下:www.2cto.com private ReentrantReadWriteLock loc

小于号的专栏 1万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

DSA不是刷题:面向工程约束的数据结构建模系统

数据结构与算法(DSA)本质上是将业务需求转化为时空约束的建模语言,而非孤立题目的解法集合。其核心原理在于问题抽象、约束量化、结构特性匹配与渐进验证闭环。技术价值体现在支撑高并发、低延迟、内存敏感等真实工程场景,如实时热搜榜的百万QPS计数、风控系统的亿级布隆过滤器选型、秒杀库存的强一致建模等。本文聚焦DSA的工程落地路径,深入解析问题建模、约束翻译两大关键环节,结合红黑树与跳表选型、布隆过滤器与Trie树对比等典型热词案例,揭示从理论复杂度到生产级常数因子、内存布局效率的完整决策链。

weixin_30527323的博客 360

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

JVM hang时,Eden区100%,所有线程包括gc线程被 blocked分析

jstat -gcutil pid 1000  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT     5.68  36.18 100.00   1.50  11.93     14    0.555     0    0.000    0.555  5.68  36.18 100.00   1.50  1

诗剑书生的专栏 1万+

linux进程hang住,一次进程hang住问题分析。。。

这两天有同学使用数据校验工具时发现进程hang住了,也不知道什么原因,我简单看了看进程堆栈,问题虽然很简单,但能导致程序hang住,也一定不是小问题。简单说明下程序组件的结构,程序由两部分构成,dbchk和dbchk_inner,dbchk采用python代码实现,dbchk_inner采用C语言实现。dbchk负责并发控制,dbchk_inner则负责具体的校验任务。用户通过运行dbchk命令即...

weixin_34844709的博客 1864

java 线程 死循环的线程_JAVA 1.7 多线程HashMap死循环

条件:多线程同时保存数据,同时触发HashMap扩容;存在某一节点上的链表内至少2个节点在新算的位置上相同;原因:1.7 HashMap采用头插法,以至于存入顺序与读取(保存链表内)的顺序相反;当第一个线程完成扩容计算后,并没有马上把原数组替换成新数组,所以其他线程还会继续操作原数组,但是,原数组里的链表顺序已经改变;其他线程执行扩容计算时,再次改变链表顺序,导致产生环路。do {Entry ne...

weixin_42367472的博客 287

大无语!线程池使用不当导致的hang死问题

最近开发了一个小功能里面用到了线程池来提高处理速度,但是发现点击按钮之后程序并没有出现本应该打印的日志。计算机没有玄学,一切问题都有其原因,找到它记录下来才能一点点提升。于是就查下这个问题吧。代码大致路基如下,创建了一个核心线程数=1,最大线程=10的线程池。点击按钮之后会将一个任务放到现成池中,然后这个任务又会for循环的去创建子任务放到同一个线程池中。父任务等待子任务的运行结果。java复制代码。

wdj_yyds的博客 544

你的也是我的。3例ko多线程,局部变量透传

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。java中的threadlocal,是绑定在线程上的。你在一个线程中set的值,在另外一个线程...

小姐姐味道 820

HashMap对象重复赋值在多线程中的教训

    分享个教训,因为这个小错误,我付出了三天时间来找原因,最后发现时,才如梦方醒:     上下文:用多个线程从一个ConurrentLinkedQueue里读文件名字,解析出文件名里的日期,然后把名字和日期放到另一个ConurrentLinkedQueue里,线程是这样写的:      HashMap nameDateMap = new HashMap(2); try{ ...

879
上一篇: GNU编译优化级别-O -O1 -O2 -O3
下一篇: C++及C中的 string char指针及char数组
程序员悟空
博客等级 码龄18年 65粉丝 52原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值