我当前正在运行一个高度并发的基准测试,它从 Java 集合中访问 ConcurrentSkipList
。我发现线程在该方法中被阻塞,更准确地说是在这里:
java.util.concurrent.ConcurrentSkipListMap.doGet(ConcurrentSkipListMap.java:828)
java.util.concurrent.ConcurrentSkipListMap.get(ConcurrentSkipListMap.java:1626)
(这是通过以超过 10 秒的间隔打印每个单独线程的堆栈跟踪来获得的)。几分钟后问题仍未解决
这是集合的预期行为吗?哪些并发的其他集合可能会遇到阻塞?
经过测试,我表现出与 ConcurrentHashMap 类似的行为:
java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:994)
最佳答案
这很可能是一个虚假的结果。
当您要求 Java 转储其所有当前堆栈跟踪时,它会告诉每个线程在到达屈服点时等待,然后捕获跟踪,然后恢复所有线程。正如您可以想象的那样,这意味着屈服点在这些迹线中的比例过高;其中包括同步方法、 volatile
访问等。ConcurrentSkipListMap.head
是一个 volatile
字段,在doGet
中访问.
参见this paper进行更详细的分析。
Solaris Studio 有一个探查器,可以从操作系统捕获堆栈跟踪并将其转换为 Java 堆栈跟踪。这消除了屈服点的偏差,并为您提供更准确的结果;您可能会发现 doGet
几乎完全消失了。我只是运气好才在 Linux 上运行过它,但即便如此,它也不是开箱即用的。如果您有兴趣,请在评论中询问我如何设置,我很乐意提供帮助。
作为一种更简单的方法,您可以使用 System.nanoTime()
包装对 ConcurrentSkipList.get
的调用,以检查这是否真的是您的时间所在正在去。计算出您在该方法上花费了多少时间,并确认它是否符合您的预期,因为分析器显示您在该方法上花费了一定百分比的时间。
无耻自插:我创建了一个simple program that demonstrates this几个月前在工作中进行演示。如果您针对探查器运行它,它应该显示 SpinWork.work
出现很多,而 HardWork.work
根本不出现 - 即使后者实际上需要更多的时间。它不包含屈服点。
关于java - 阻塞行为 - Java 并发数据结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19638005/