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

use*_*513 0 java collections concurrency multithreading synchronization

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

java.util.concurrent.ConcurrentSkipListMap.doGet(ConcurrentSkipListMap.java:828)    
java.util.concurrent.ConcurrentSkipListMap.get(ConcurrentSkipListMap.java:1626)
Run Code Online (Sandbox Code Playgroud)

(这是通过超过10秒的间隔打印每个单独线程的堆栈跟踪获得的).几分钟后仍未解决

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

经过测试,我表现出与ConcurrentHashMaps 相似的行为:

java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:994)
Run Code Online (Sandbox Code Playgroud)

ysh*_*vit 5

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

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

有关更详细的分析,请参阅此文章.

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

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

无耻的自我插件:我创建了一个简单的程序,几个月前在工作中进行演示.如果你对一个分析器运行它,它应该显示SpinWork.work出很多,但HardWork.work根本不显示 - 即使后者实际上需要更多的时间.它不包含屈服点.

  • 如果你想用nanoTime()包装get(),请注意后者可能需要1μs到10ns,具体取决于硬件和操作系统.另请参阅nanoTime的粒度. (2认同)