大家不用关心程序实际逻辑是否正确,只是好奇,为什么这个程序会hang住不运行了?而且CPU会占用100%

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


t1.start();
t2.start();

}


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


Dump thread会看到,程序hang到:

"Thread-1" prio=6 tid=0x00c70bd8 nid=0x914 runnable [0x02ebf000..0x02ebfc68]
at java.util.HashMap.put(HashMap.java:420)
at TestLock$2.run(TestLock.java:20)

"Thread-0" prio=6 tid=0x00c70a50 nid=0x578 runnable [0x02e7f000..0x02e7fb68]
at java.util.HashMap.put(HashMap.java:420)
at TestLock$1.run(TestLock.java:11)

Feedback

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 17:53 by nobody
HashMap 不是线程安全的;请换成Hashtable。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 18:23 by Scud(飞云小侠)
我在本地运行了一下 没发现死锁.

你可以用visualvm 检测一下, 它可以发现是否死锁, 已经死锁的行数等信息

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 20:07 by stone2083
"Thread-1" prio=6 tid=0x00c70bd8 nid=0x914 runnable
两个线程都是runnable状态.

这程序,理论上,不应该出现死锁的.

# re: 谁能帮忙解释一下为什么这个程序会死锁?[未登录]  回复  更多评论   

2010-08-04 20:39 by 行云流水
不会发生死锁。不是线程安全的hashmap,貌似你也没有用到锁的概念。只是到最后map里面的数据大于50000.

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 21:02 by 天快黑了
@nobody
这个知道,现在是想搞明白程序为什么会hang在那不走了

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 21:03 by 天快黑了
@Scud(飞云小侠)
应该是与机器有关,我公司机器,使用300个循环就会hang,你可以试着把循环调大一点试试看。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 21:30 by stone2083
突然记起来了.请参考这篇文章:
http://sdh5724.javaeye.com/blog/619130

在自己电脑上,一直无法重现这个问题(无论加大循环,或者增加线程数),明天回公司再去看看.

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-04 22:02 by 天快黑了
@stone2083
非常感谢提供的链接。

对,可能是多个线程同时修改HashMap,导致,HashMap内部死循环了。不过奇怪,不管怎么dump thread,都是hang在HashMap.java:420,而查看HashMap的代码,怎么也想不通这个地方会hang住。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 09:37 by peacess
线程还在运行,而main函数已经退了,

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 10:50 by 猪儿笨笨
你在put方法420行的循环中 加一句
System.out.println(e+" "+e.next);

就会产生一个死循环,即e=e.next.next。
应该是多线程下,进行hash处理的时候出现的。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 12:31 by 天快黑了
@猪儿笨笨
e.next访问的是e的一个属性,怎么会相当于e.next.next?

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 19:06 by bigwesthorse
我改成了concurrenthashmap还是会出现,只是出现的频率降低了不少,大家可以陆续开上千个进程试一下
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
start javaw TestLock
...........etc

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 19:13 by bigwesthorse
@bigwesthorse
只有hashtable可以顺利地通过1429个进程测试,哈哈,似乎看到了一点让人惊喜的东西

# re: 谁能帮忙解释一下为什么这个程序会死锁?[未登录]  回复  更多评论   

2010-08-05 20:58 by kafka0102
死循环的代码在hashmap的transfer方法:
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;
do {
final Entry<K,V> next = e.next;
final int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
我的分析是:死循环就在while中,对同一个旧table中的e,一个线程执行了 e.next = newTable[i];(还没到 e = next;),另一个才到 final Entry<K,V> next = e.next;,结果后到的线程接着执行e = next;造成e=e.next,死循环了。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 22:00 by 猪儿笨笨
楼上的
不是e=e.next
而是e=e.next.next
不过你的分析应该是对的,就是在根据hashcode计算位置的时候,因为并发的原因出现问题了。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 22:01 by 猪儿笨笨
@天快黑了

这个很正常的,因为e.next相当于一个链表指针,指向下一个点,它们是同一个例的实类。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 22:03 by 猪儿笨笨
另外,关于HashMap的实现,已经说了是线程不安全的,多线程下,出现问题是正常的。

Hashtable一直线程安全,可以放心用。
但说concurrenthashmap不对,这个应该不会。
回头也试一下再来讨论。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-05 22:38 by 猪儿笨笨
用了concurrenthashmap,循环5000次,没有出现问题。

public TestLock() {


for (int i = 0; i < 5000; i++) {

final int count=i;

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

}
System.out.println(count+" over");
}
};

t1.start();
}
}

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-06 09:27 by bigwesthorse
@猪儿笨笨
对于concurrenthashmap
用一个批处理写1000多个start javaw TestLock
肯定会出现的,我说了概率很低,基本上两三百个程序会有个卡死
你就等着那个批处理狂跑就行了,1到2分钟之后,你就会感到cpu100%了
被某个进程霸占了,一直hang在那里

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-06 15:36 by 猪儿笨笨
没有明白楼上说什么意思?
这样说吧,是否在一个JVM内,还是说同时启动多个JVM?
如果多个JVM,就很难说有什么问题,比如说用光了资源,等等。

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-06 16:16 by bigwesthorse
@猪儿笨笨
就是这个程序是否出现死锁要看rp的,跑个200多次才会出现一次
我用批处理连续跑了上千次,出现了好几次死锁的情况

# re: 谁能帮忙解释一下为什么这个程序会死锁?  回复  更多评论   

2010-08-07 10:51 by 天快黑了
感谢大家的回复,这个问题确实很难解释清楚。我们只能注意,多线程下千万不要使用非线程安全的对象。

# re: 谁能帮忙解释一下为什么这个程序会死锁?[未登录]  回复  更多评论   

2010-10-20 16:59 by littleJava
@猪儿笨笨
ConcurrentHashMap只是保证Happens-before关系,勉强的线程安全,如果需要保证数据一致性,还是用Collections.synchronizedMap(Map map)方法,利用的synchronized机制,规划好代码流程来实现线程安全

只有注册用户登录后才能发表评论。


网站导航:
 

posts - 5, comments - 25, trackbacks - 0, articles - 1

Copyright © 天快黑了