为什么执行HashMap操作时ExecutorService死锁? [英] Why does ExecutorService deadlock when performing HashMap operations?

查看:275
本文介绍了为什么执行HashMap操作时ExecutorService死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当运行下面的类时,ExecutionService通常会死锁。

  import java.util.ArrayList; 
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ExecutorTest {
public static void main(final String [] args)throws InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(10);

final HashMap< Object,Object> map = new HashMap< Object,Object>();
final Collection< Callable< Object>> actions = new ArrayList< Callable< Object>>();
int i = 0;
while(i ++< 1000){
final Object o = new Object();
actions.add(new Callable< Object>(){
public Object call()throws Exception {
map.put(o,o);
return null;
}
});
actions.add(new Callable< Object>(){
public Object call()throws Exception {
map.put(new Object(),o);
return null ;
}
});
actions.add(new Callable< Object>(){
public Object call()throws Exception {
for(Iterator iterator = map.entrySet()。iterator(); iterator.hasNext ();){
iterator.next();
}
return null;
}
});
}
executor.invokeAll(actions)
System.exit(0);
}

}

或者更好 - 我怎么能写一个测试,以确保定制抽象地图的实现是线程安全的? (一些实现有多个映射,另一个委托给缓存实现等)



一些背景:
这发生在Java 1.6.0_04和1.6.0_07 。我知道问题来自sun.misc.Unsafe.park():




  • 我可以重现我的Core2 Duo 2.4Ghz

  • 我可以在我的Core2 Quad上调试,但是我已经把它挂在RDP上,所以不能得到
    a堆栈跟踪到明天



下面的大多数答案都是关于HashMap的非线程安全性,但我在HashMap中找不到任何锁定的线程都在ExecutionService代码(和Unsafe.park())中。我将仔细检查明天的线程。



这一切都是因为一个自定义的抽象Map实现不是线程安全的,所以我设置确保所有的实现都是线程安全的。本质上,我想确保我对ConcurrentHashMap等的理解正是我的期望,但是发现ExecutionService奇怪地缺少...

解决方案

你使用一个着名的非线程安全类并抱怨死锁。



此外, ExecutionService

$ b奇怪缺乏



这是一个常见的误解,使用例如 a HashMap 一些陈旧的数据。请参阅一个美丽的比赛条件,了解如何炸毁你的



了解为什么会发生这种情况是一个非常棘手的过程,需要知道JVM和类库的内部结构。



对于ConcurrentHashMap,只需读取 javadoc - 它应该澄清你的问题。如果没有,请查看实践中的Java并发



< hr>

更新:



我设法重现了你的情况,但并不是一个死锁。 动作之一从不完成执行。堆栈跟踪是:

 pool-1-thread-3prio = 10 tid = 0x08110000 nid = 0x22f8 runnable [0x805b0000 ] 
java.lang.Thread.State:RUNNABLE
在ExecutorTest $ 3.call(ExecutorTest.java:36)
在java.util.concurrent.FutureTask $ Sync.innerRun(FutureTask.java :303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
在java.util.concurrent.ThreadPoolExecutor $ Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor $ Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

看起来像我链接到的确切情况 - HashMap被调整大小,并且由于调整大小的内部机制迭代器陷入无限循环。 / p>

当发生这种情况时, invokeAll 永远不会返回,程序挂起。但它既不是死锁,也不是活锁,而是一种竞争条件。


When running the following class the ExecutionService will often deadlock.

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ExecutorTest {
public static void main(final String[] args) throws InterruptedException {
    final ExecutorService executor = Executors.newFixedThreadPool(10);

    final HashMap<Object, Object> map = new HashMap<Object, Object>();
    final Collection<Callable<Object>> actions = new ArrayList<Callable<Object>>();
    int i = 0;
    while (i++ < 1000) {
        final Object o = new Object();
        actions.add(new Callable<Object>() {
            public Object call() throws Exception {
                map.put(o, o);
                return null;
            }
        });
        actions.add(new Callable<Object>() {
            public Object call() throws Exception {
                map.put(new Object(), o);
                return null;
            }
        });
        actions.add(new Callable<Object>() {
            public Object call() throws Exception {
                for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
                    iterator.next();
                }
                return null;
            }
        });
    }
    executor.invokeAll(actions);
    System.exit(0);
}

}

So why does this happen? Or better yet - how can I write a test to ensure that implementations of an custom abstract map are thread safe? (Some implementations have multiple maps, another delegates to a cache implementation etc)

Some background: this occurs under Java 1.6.0_04 and 1.6.0_07 on Windows. I know that the problem comes from sun.misc.Unsafe.park():

  • I can reproduce the problem on my Core2 Duo 2.4Ghz laptop but not while running in debug
  • I can debug on my Core2 Quad at work, but I've hung it over RDP, so won't be able to get a stack trace until tomorrow

Most answers below are about the non-thread safety of HashMap, but I could find no locked threads in HashMap - it was all in the ExecutionService code (and Unsafe.park()). I shall closely examine the threads tomorrow.

All this because a custom abstract Map implementation was not thread-safe so I set about ensuring that all implementations would be thread-safe. In essence, I'm wanting to ensure that my understanding of ConcurrentHashMap etc are exactly what I expect, but have found the ExecutionService to be strangely lacking...

解决方案

You're using an well-known not-thread-safe class and complaining about deadlock. I fail to see what the issue is here.

Also, how is the ExecutionService

strangely lacking

?

It's a common misconception that by using e.g. a HashMap you will at most get some stale data. See a beautiful race condition about how you can blow up your JVM by doing just that.

Understanding why this happens is a very tricky process and requires knowledge of the internals of both the JVM and the class libraries.

As for the ConcurrentHashMap, just read the javadoc - it should clarify your questions. If not, take a look at Java Concurrency in Practice.


Update:

I managed to reproduce your situation, but it's not a deadlock. One of the actions never completes execution. The stack trace is:

"pool-1-thread-3" prio=10 tid=0x08110000 nid=0x22f8 runnable [0x805b0000]
java.lang.Thread.State: RUNNABLE
at ExecutorTest$3.call(ExecutorTest.java:36)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

It looks like the exact case I linked to - the HashMap gets resized and due to the internal mechanics of resizing the iterator gets stuck in an infinite loop.

When this happens, invokeAll never returns and the program hangs. But it's neither a deadlock, nor a livelock, but a race condition.

这篇关于为什么执行HashMap操作时ExecutorService死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆