`

一个验证HashMap在多线程环境下线程不安全的例子及dump分析

阅读更多

实例: 

package com.bijian.study.hashmap;

import java.util.HashMap;

public class TestLock {

	private HashMap map = new HashMap();

	public TestLock() {
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 50000; i++) {
					map.put(new Integer(i), i);
				}
				System.out.println("t1 over");
			}
		};

		Thread t2 = new Thread() {
			public void run() {
				for (int i = 0; i < 50000; i++) {
					map.put(new Integer(i), i);
				}

				System.out.println("t2 over");
			}
		};
		
		Thread t3 = new Thread() {
			public void run() {
				for (int i = 0; i < 50000; i++) {
					map.put(new Integer(i), i);
				}

				System.out.println("t3 over");
			}
		};
		
		Thread t4 = new Thread() {
			public void run() {
				for (int i = 0; i < 50000; i++) {
					map.put(new Integer(i), i);
				}

				System.out.println("t4 over");
			}
		};
		
		Thread t5 = new Thread() {
			public void run() {
				for (int i = 0; i < 50000; i++) {
					map.put(new Integer(i), i);
				}

				System.out.println("t5 over");
			}
		};
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}

	public static void main(String[] args) {
		new TestLock();
	}
}

 

 运行结果:

1.  报如下错误

t5 over
t4 over
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 21604
at java.util.HashMap.addEntry(Unknown Source)
at java.util.HashMap.put(Unknown Source)
at com.bijian.study.hashmap.TestLock$1.run(TestLock.java:13)
t3 over
t2 over

2.这个程序会hang住不运行了,而且CPU会占用100%

 
下面我们对第二种错误进行分析

 

       JDK自带工具分析

1.       jconsole.exe(此工具未发现能分析Dump Thread的功能

点击运行

选中相应的进程,点“连接”即可进入Java监视和管理控制台,如下所示:

点击“线程”页签,查看线程情况

 

2.       jpsjstack <pid>

a.       cmd窗口,将JDK下的jps.exe拖入回车运行,获得相应线程的PID

这里TestLockPID5048

b.       然后在cmd中输入jstack 5048,回车,就可看到Dump thread信息

 

3.       jvisualvm.exe

点击运行JDK下的jvisualvm.exe

启动后,点击“线程Dump(T)”选项,即可看到Dump thread信息

  

Dump thread会看到,程序hang到:

"DestroyJavaVM" prio=6 tid=0x011ffc00 nid=0x11e0 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE
"Thread-3" prio=6 tid=0x03f46c00 nid=0x1520 runnable [0x041cf000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap.transfer(Unknown Source)
        at java.util.HashMap.resize(Unknown Source)
        at java.util.HashMap.addEntry(Unknown Source)
        at java.util.HashMap.put(Unknown Source)
        at com.bijian.study.hashmap.TestLock$4.run(TestLock.java:42)
"Low Memory Detector" daemon prio=6 tid=0x01a93800 nid=0xac runnable [0x00000000]
   java.lang.Thread.State: RUNNABLE

      CPU利用率过高一般是因为出现了出现了死循环,导致部分线程一直运行,占用cpu时间。问题原因就是HashMap是非线程安全的,多个线程put的时候造成了某个keyEntry key List的死循环,问题就这么产生了。

当另外一个线程get 这个Entry List 死循环的key的时候,这个get也会一直执行。最后结果是越来越多的线程死循环,最后导致服务器dang掉。我们一般认为HashMap重复插入某个值的时候,会覆盖之前的值,这个没错。但是对于多线程访问的时候,由于其内部实现机制(在多线程环境且未作同步的情况下,对同一个HashMap做put操作可能导致两个或以上线程同时做rehash动作,就可能导致循环键表出现,一旦出现线程将无法终止,持续占用CPU,导致CPU使用率居高不下),就可能出现安全问题了。

 

三种方法解决此问题

1.Hashtable替换HashMap

private Hashtable map = new Hashtable();

替换

private HashMap map = new HashMap();

说明:

Hashtable 是同步的,但由迭代器返回的 Iterator 和由所有 Hashtable “collection 视图方法返回的 Collection listIterator 方法都是快速失败的:在创建 Iterator 之后,如果从结构上对 Hashtable 进行修改,除非通过 Iterator 自身的移除或添加方法,否则在任何时间以任何方式对其进行修改,Iterator 都将抛出 ConcurrentModificationException。因此,面对并发的修改,Iterator 很快就会完全失败,而不冒在将来某个不确定的时间发生任意不确定行为的风险。由 Hashtable 的键和值方法返回的 Enumeration 是快速失败的。

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测程序错误。

 

2.Collections.synchronizedMapHashMap包装起来

private Map map = Collections.synchronizedMap(new HashMap());

替换

private HashMap map = new HashMap();

说明:

返回由指定映射支持的同步(线程安全的)映射。为了保证按顺序访问,必须通过返回的映射完成对底层映射的所有访问。

在返回的映射或其任意 collection 视图上进行迭代时,强制用户手工在返回的映射上进行同步:

  Map m = Collections.synchronizedMap(new HashMap());

 

 ...

  Set s = m.keySet();  // Needn't be in synchronized block

      ...

  synchronized(m) {  // Synchronizing on m, not s!

      Iterator i = s.iterator(); // Must be in synchronized block

      while (i.hasNext())

          foo(i.next());

  }

       不遵从此建议将导致无法确定的行为。

如果指定映射是可序列化的,则返回的映射也将是可序列化的。

 

3.ConcurrentHashMap替换HashMap

private ConcurrentHashMap map = new ConcurrentHashMap();

替换

private HashMap map = new HashMap();

说明:

       支持检索的完全并发和更新的所期望可调整并发的哈希表。此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有操作都是线程安全的,但检索操作必锁定,并且支持以某种防止所有访问的方式锁定整个表。此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。

检索操作(包括 get)通常不会受阻塞,因此,可能与更新操作交迭(包括 put remove)。检索会影响最近完成的更新操作的结果。对于一些聚合操作,比如 putAll clear,并发检索可能只影响某些条目的插入和移除。类似地,在创建迭代器/枚举时或自此之后,Iterators Enumerations 返回在某一时间点上影响哈希表状态的元素。它们不会抛出 ConcurrentModificationException。不过,迭代器被设计成每次仅由一个线程使用。

  • 大小: 18.2 KB
  • 大小: 29.8 KB
  • 大小: 56 KB
  • 大小: 82.4 KB
  • 大小: 8.1 KB
  • 大小: 102.2 KB
  • 大小: 106.8 KB
分享到:
评论
1 楼 magicyang919 2014-11-13  
我测的一直没出现cpu100%的情况。

相关推荐

    多线程、JVM复习&面试&强化训练100题

    如果一个类在多线程环境下能够安全地使用,没有出现数据不一致或数据污染的情况,这个类就被认为是线程安全的。 JVM(Java虚拟机)是运行Java字节码的虚拟机进程,它在不同的操作系统上提供了一致的运行环境。JVM的...

    15个顶级JAVA多线程面试题及回答[文].pdf

    由于【部分内容】中提供的信息非常杂乱,缺乏连贯性和上下文,因此下面的知识点将基于Java多线程及并发编程的通用知识点进行构建,以确保满足15个顶级JAVA多线程面试题及回答的知识要求。 首先,在Java多线程面试中...

    idea git提交内存溢出后dump文件

    在这种情况下,JVM为了帮助开发者分析问题,会生成一个堆转储文件(Heap Dump),如`heapDump.hprof`,该文件包含了JVM在特定时刻的内存快照,包括所有对象、类加载器、线程和内存分配等详细信息。 分析`heapDump....

    15个顶级Java多线程面试题及回答.docx

    **解答:** 原子操作是指在多线程环境下不会被中断的操作,要么全部执行完成,要么不执行。Java提供了`AtomicInteger`等类来实现原子操作,这些类内部使用了`Unsafe`类中的CAS算法来保证原子性。 ```java import ...

    一本经典的多线程书籍 Java并发编程 设计原则与模式 第二版 (英文原版)

    3. **并发容器**:书中深入讨论了Java并发容器,如ArrayList、LinkedList、HashSet、HashMap以及并发优化过的ConcurrentHashMap、CopyOnWriteArrayList和CopyOnWriteArraySet等,以及它们在多线程环境下的性能和使用...

    JVisualVM简介与内存泄漏实战分析

    一个典型的内存泄漏例子是在程序中持续创建对象并将它们保留在一个集合中,而这些对象永远不会被释放。例如,下面的代码示例展示了如何通过循环创建大量的字节数组,并将它们添加到HashMap中,从而模拟内存泄漏。 `...

    Java 性能分析

    3. **并发编程**:理解Java的多线程模型,避免死锁、竞态条件和活锁,正确使用synchronized和volatile关键字,是保证并发性能的基础。 4. **垃圾收集**:了解Java的垃圾回收机制,如新生代、老年代、CMS、G1、ZGC等...

    计算机后端-Java-Java高并发从入门到面试教程-容思路.zip

    同步和互斥是确保多线程环境下数据一致性的重要手段,Java提供了多种同步机制,如synchronized关键字、Lock接口(ReentrantLock、ReadWriteLock等)以及原子类(AtomicInteger、AtomicLong等)。 接下来,我们要...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【多线程】什么是线程安全与非线程安全 72 【多线程】多线程的实现方式Thread、Runnable、Callable 72 【多线程】实现Runnable接口与继承Thread类比较 73 【多线程】线程状态转换 74 【多线程】线程的调度 75 线程...

    java基础面试考察点.pdf

    Java 基础面试考察点是 Java 开发人员必须掌握的知识点,涵盖了 Java 基础知识、多线程、JVM 相关知识点等几个方面。 Java 基础知识点 1. Java 内部集合类:ArrayList 和 LinkedList 的区别,数据结构实现,扩容...

    java面试评价表.doc

    1. 多线程如何启动和停止一个线程:可以使用 Thread 类或 Runnable 接口来启动线程,停止线程可以使用 stop() 或 interrupt() 方法。 2. 影响线程安全的原因是什么?如何避免?线程安全问题是指多个线程访问共享资源...

    java 高并发解决 思路

    - **CountDownLatch**:一次性计数器,用于多线程同步。 - **CyclicBarrier/Phaser**:循环栅栏,允许一组线程等待其他线程到达某个点后继续执行。 - **Semaphore**:信号量,用于限制同时访问的线程数量。 5. *...

    一线互联网公司面试题目

    ConcurrentHashMap是线程安全的选择,更适合多线程环境。 5-6. **线程状态与阻塞**:线程有新建、就绪、运行、阻塞和死亡五种状态。阻塞方式包括wait()、sleep()等,其中wait()需在同步块中使用,sleep()则不会释放...

    阿里、京东、美团、腾讯、百度大厂2023年面试集锦

    排查 OOM 的问题可以使用内存dump、GC日志、线程dump 等手段来分析系统的内存使用情况。 其他 1. IO 密集=NCPU*2 是怎么计算出来的?IO 密集是指系统的IO 操作密集度,可以根据系统的CPU 数量和IO 操作的频率来...

    JVM面试专题.zip

    - **Java内存模型(JMM)**:规定了线程之间的共享变量访问规则,确保多线程环境下的一致性。 - **volatile关键字**:保证可见性和有序性,但不保证原子性。 - **synchronized关键字**:实现线程同步,保证原子性和...

    JDK学习笔记的全部

    Java支持多线程编程,通过Thread类和Runnable接口实现。线程同步和通信机制,如synchronized关键字、wait/notify、锁(ReentrantLock)等,确保了并发安全。 8. 异常处理 Java的异常处理机制通过try-catch-finally...

    2022面试题1java背诵版本.doc

    10. **可重复读解决的问题**:防止脏读和不可重复读,即在同一个事务中多次读取同一数据,结果始终保持一致。 11. **SQL慢查询优化**:包括创建合适索引、优化查询语句、限制返回数据量、使用连接(JOIN)替代子...

    Dump_Yard-Java:我在学习时做的一些多余的练习程序

    "Dump_Yard-Java"这个项目很可能是一位程序员在学习Java时创建的一个代码仓库,包含了他/她在实践过程中编写的额外练习程序。通过这个项目,我们可以探讨一些Java编程的基础知识以及可能涉及的高级主题。 首先,让...

Global site tag (gtag.js) - Google Analytics