java - 阻塞行为 - Java 并发数据结构

标签 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 类似的行为:

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/

相关文章:

java - 如何处理服务器和客户端中不同的数据包?

java - 在主线程上显示 XML

java - RxJava 观察调用/订阅线程

java - 为什么我的 Object ArrayList 循环结果不正确?

java - 使用我创建的 Exception 类捕获抛出的异常

java - EnumMap 与枚举值

c++ - `clock()` 给出通常的时钟而不是 CPU 时钟

java - 使用 Java 集合的应用程序

java - 初始化 java 集合,将泛型留空,例如: HashMap<A, B> hM = HashMap<>();

c# - 在 C# 中处理可变集合键