作者:小明明哦丫头_517 | 来源:互联网 | 2023-09-23 15:40
单线程化的ConcurrentHashMap的性能要比同步的HashMap的性能稍好一些,而且在并发应用中,这种作用就十分明显了。ConcurrentHashMap的实现,假定大多数常用的操
单线程化的ConcurrentHashMap的性能要比同步的HashMap的性能稍好一些,而且在并发应用中,这种作用就十分明显了。ConcurrentHashMap的实现,假定大多数常用的操作都是获取已存在的某个值,因此它的优化是针对get操作,提供最好的性能和并发性。
同步的Map实现中,可伸缩性最主要的阻碍在于整个Map存在一个锁,所以一次只有一个线程能够访问map,从另一方面来看,ConcurrentHashMap并没有对成功的读操作加锁,对写操作和真正需要锁的读操作使用了分离锁的方法。因此,多线程能够并发访问Map,而不被阻塞。
图11.3阐释了几种Map实现不同的可扩展性:ConcurrentHashMap,ConcurrentSkipListMap,以及由synchronizedMap包装的HashMap和TreeMap。前两个的设计都是线程安全的;后两个通过同步的包装器实现了线程安全。每次运行中,N个线程并发执行一个紧密的循环,随机选择key,并尝试获取相应的值。如果不存在符合结果,它会增加Map的可能性p = .6,如果存在,它会减少可能性p = .02。这个测试在预发布的Java 6,8路 Sparc V880系统下进行,并且这个图表展示了规范为单线程情况的ConcurrentHashMap的吞吐量。(并发容器和同步容器间的可伸缩性差别比Java 5.0还要明显。)
ConcurrentHashMap和ConcurrentSkipListMap的数据显示,它们能很好地应对数量很大的线程;吞吐量随着线程数量的增加而增长。尽管图11.3中线程的数量看起来不是很大,但是与普通的程序相比,这个测试程序为每个线程都产生了更多的竞争,因为它除了向Map施加压力,几乎没有做任何事情;而真正的程序会在每个迭代中进行额外的线程本地工作。
同步容器的数量并不是越多越好。单线程的情况与ConcurrentHashMap一致,但是一旦负载由多数为非竞争的情况变成多数为竞争性的情况——这里是两个线程——同步的容器就会很糟糕。这在锁竞争的代码行为中是很常见的。只要竞争小,每个操作所花费的时间取决于真正工作的时间,吞吐量会因为线程数的增加而增加。一旦竞争变得激烈,每个操作花费的时间就由上下文切换和调度延迟决定了,并且加入更多的线程不会对吞吐量有什么帮助。
图11.3 比较不同Map实现间的可伸缩性