阻止行为 - 并发数据结构Java

时间:2013-10-28 14:52:53

标签: java multithreading collections concurrency synchronization

我目前正在运行一个高度并发的基准测试,它从Java集合中访问ConcurrentSkipList。我发现线程在该方法中被阻塞,更准确地说:

java.util.concurrent.ConcurrentSkipListMap.doGet(ConcurrentSkipListMap.java:828)    
java.util.concurrent.ConcurrentSkipListMap.get(ConcurrentSkipListMap.java:1626)

(这是通过超过10秒的间隔打印每个单独线程的堆栈跟踪获得的)。分钟

后仍未解决

这是收藏品的预期行为吗?什么是可能遇到阻塞的并发其他集合?

经过测试,我表现出与ConcurrentHashMap s类似的行为:

java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:994)

3 个答案:

答案 0 :(得分:5)

这很可能是一个虚假的结果。

当您要求Java转储其所有当前堆栈跟踪时,它会告诉每个线程在到达屈服点时等待,然后它捕获跟踪,然后它恢复所有线程。可以想象,这意味着屈服点在这些痕迹中过度表现;其中包括同步方法,volatile次访问等。ConcurrentSkipListMap.headvolatile字段,可在doGet中访问。

有关更详细的分析,请参阅this paper

Solaris Studio有一个分析器,它从操作系统中捕获堆栈跟踪并将它们转换为Java堆栈跟踪。这消除了对屈服点的偏差,并为您提供更准确的结果;你可能会发现doGet几乎完全消失了。我只是运气在Linux上运气,即便如此,它也不是开箱即用的。如果您有兴趣,请在评论中询问我如何设置,我很乐意提供帮助。

作为一种更简单的方法,您可以将ConcurrentSkipList.get的{​​{1}}打包到System.nanoTime(),以便检查这是否真的是您的时间。弄清楚你花在这种方法上的时间,并确认这是否与你期望的一致,因为探查者说你在这种方法中花费了这么多的时间。

无耻的自我插件:几个月前我创建了一个simple program that demonstrates this来进行演示。如果您针对分析器运行它,它应该显示SpinWork.work显示很多,而HardWork.work根本不显示 - 即使后者实际上需要更多时间。它不包含屈服点。

答案 1 :(得分:0)

嗯,它并没有以最真实的形式阻挡。阻塞意味着线程活动的暂停。 ConcurrentSkipListMap是非阻塞的,它会旋转直到成功。但它也保证它最终会成功(也就是说它不应该进入无限循环)

话虽这么说,除非你做了很多很多事情并且许多次异步,我不明白你怎么能花这么多时间在这种方法上。

如果您可以通过示例重新创建它并与我们分享可能会有所帮助。

答案 2 :(得分:0)

ConcurrentHashMap.get是易失性读取,这意味着CPU必须先完成所有未完成的写操作才能执行读操作。这称为STORE / LOAD屏障。根据其他线程/核心的进度,这可能需要很长时间。请参阅https://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html