我目前正在运行一个高度并发的基准测试,它从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)
答案 0 :(得分:5)
这很可能是一个虚假的结果。
当您要求Java转储其所有当前堆栈跟踪时,它会告诉每个线程在到达屈服点时等待,然后它捕获跟踪,然后它恢复所有线程。可以想象,这意味着屈服点在这些痕迹中过度表现;其中包括同步方法,volatile
次访问等。ConcurrentSkipListMap.head
,volatile
字段,可在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。