实例:
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. jps、jstack <pid>
a. 打cmd窗口,将JDK下的jps.exe拖入回车运行,获得相应线程的PID
这里TestLock的PID是5048
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的时候造成了某个key值Entry 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.synchronizedMap将HashMap包装起来
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。不过,迭代器被设计成每次仅由一个线程使用。
相关推荐
如果一个类在多线程环境下能够安全地使用,没有出现数据不一致或数据污染的情况,这个类就被认为是线程安全的。 JVM(Java虚拟机)是运行Java字节码的虚拟机进程,它在不同的操作系统上提供了一致的运行环境。JVM的...
由于【部分内容】中提供的信息非常杂乱,缺乏连贯性和上下文,因此下面的知识点将基于Java多线程及并发编程的通用知识点进行构建,以确保满足15个顶级JAVA多线程面试题及回答的知识要求。 首先,在Java多线程面试中...
在这种情况下,JVM为了帮助开发者分析问题,会生成一个堆转储文件(Heap Dump),如`heapDump.hprof`,该文件包含了JVM在特定时刻的内存快照,包括所有对象、类加载器、线程和内存分配等详细信息。 分析`heapDump....
**解答:** 原子操作是指在多线程环境下不会被中断的操作,要么全部执行完成,要么不执行。Java提供了`AtomicInteger`等类来实现原子操作,这些类内部使用了`Unsafe`类中的CAS算法来保证原子性。 ```java import ...
3. **并发容器**:书中深入讨论了Java并发容器,如ArrayList、LinkedList、HashSet、HashMap以及并发优化过的ConcurrentHashMap、CopyOnWriteArrayList和CopyOnWriteArraySet等,以及它们在多线程环境下的性能和使用...
一个典型的内存泄漏例子是在程序中持续创建对象并将它们保留在一个集合中,而这些对象永远不会被释放。例如,下面的代码示例展示了如何通过循环创建大量的字节数组,并将它们添加到HashMap中,从而模拟内存泄漏。 `...
3. **并发编程**:理解Java的多线程模型,避免死锁、竞态条件和活锁,正确使用synchronized和volatile关键字,是保证并发性能的基础。 4. **垃圾收集**:了解Java的垃圾回收机制,如新生代、老年代、CMS、G1、ZGC等...
同步和互斥是确保多线程环境下数据一致性的重要手段,Java提供了多种同步机制,如synchronized关键字、Lock接口(ReentrantLock、ReadWriteLock等)以及原子类(AtomicInteger、AtomicLong等)。 接下来,我们要...
【多线程】什么是线程安全与非线程安全 72 【多线程】多线程的实现方式Thread、Runnable、Callable 72 【多线程】实现Runnable接口与继承Thread类比较 73 【多线程】线程状态转换 74 【多线程】线程的调度 75 线程...
Java 基础面试考察点是 Java 开发人员必须掌握的知识点,涵盖了 Java 基础知识、多线程、JVM 相关知识点等几个方面。 Java 基础知识点 1. Java 内部集合类:ArrayList 和 LinkedList 的区别,数据结构实现,扩容...
1. 多线程如何启动和停止一个线程:可以使用 Thread 类或 Runnable 接口来启动线程,停止线程可以使用 stop() 或 interrupt() 方法。 2. 影响线程安全的原因是什么?如何避免?线程安全问题是指多个线程访问共享资源...
- **CountDownLatch**:一次性计数器,用于多线程同步。 - **CyclicBarrier/Phaser**:循环栅栏,允许一组线程等待其他线程到达某个点后继续执行。 - **Semaphore**:信号量,用于限制同时访问的线程数量。 5. *...
ConcurrentHashMap是线程安全的选择,更适合多线程环境。 5-6. **线程状态与阻塞**:线程有新建、就绪、运行、阻塞和死亡五种状态。阻塞方式包括wait()、sleep()等,其中wait()需在同步块中使用,sleep()则不会释放...
排查 OOM 的问题可以使用内存dump、GC日志、线程dump 等手段来分析系统的内存使用情况。 其他 1. IO 密集=NCPU*2 是怎么计算出来的?IO 密集是指系统的IO 操作密集度,可以根据系统的CPU 数量和IO 操作的频率来...
- **Java内存模型(JMM)**:规定了线程之间的共享变量访问规则,确保多线程环境下的一致性。 - **volatile关键字**:保证可见性和有序性,但不保证原子性。 - **synchronized关键字**:实现线程同步,保证原子性和...
Java支持多线程编程,通过Thread类和Runnable接口实现。线程同步和通信机制,如synchronized关键字、wait/notify、锁(ReentrantLock)等,确保了并发安全。 8. 异常处理 Java的异常处理机制通过try-catch-finally...
10. **可重复读解决的问题**:防止脏读和不可重复读,即在同一个事务中多次读取同一数据,结果始终保持一致。 11. **SQL慢查询优化**:包括创建合适索引、优化查询语句、限制返回数据量、使用连接(JOIN)替代子...
"Dump_Yard-Java"这个项目很可能是一位程序员在学习Java时创建的一个代码仓库,包含了他/她在实践过程中编写的额外练习程序。通过这个项目,我们可以探讨一些Java编程的基础知识以及可能涉及的高级主题。 首先,让...