java面试题网

普通会员

115

帖子

83

回复

167

积分

楼主
发表于 2019-09-20 10:55:39 | 查看: 5099| 回复: 1

Java中的同步集合与并发集合有什么区别?

Java中的同步集合与并发集合有什么区别?
同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。
不管是同步集合还是并发集合他们都支持线程安全,他们之间主要的区别体现在性能和可扩展性,还有他们如何实现的线程安全上。
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他们并发的实现(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)会慢得多。造成如此慢的主要原因是锁, 同步集合会把整个Map或List锁起来,而并发集合不会。并发集合实现线程安全是通过使用先进的和成熟的技术像锁剥离。
比如ConcurrentHashMap 会把整个Map 划分成几个片段,只对相关的几个片段上锁,同时允许多线程访问其他未上锁的片段。
同样的,CopyOnWriteArrayList 允许多个线程以非同步的方式读,当有线程写的时候它会将整个List复制一个副本给它。
如果在读多写少这种对并发集合有利的条件下使用并发集合,这会比使用同步集合更具有可伸缩性。


文章来自www.wityx.com,转载请注明出处!原文地址http://www.wityx.com/post/1285_1_1.html


java面试题交流群:327440556

普通会员

115

帖子

83

回复

167

积分
沙发
发表于 2019-09-26 15:06:17

并发集合和普通集合如何区别?
并发集合常见的有ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque等。并发集合
位于 java.util.concurrent 包 下 , 是 jdk1.5 之 后 才 有 的 。
在java中有普通集合、同步(线程安全)的集合、并发集合。普通集合通常性能最高,但是不保证多线程的安全
性和并发的可靠性。线程安全集合仅仅是给集合添加了synchronized同步锁,严重牺牲了性能,而且对并发的效率就
更低了,并发集合则通过复杂的策略不仅保证了多线程的安全又提高的并发时的效率。
ConcurrentHashMap 是线程安全的 HashMap 的实现,默认构造同样有 initialCapacity 和 loadFactor 属性,
不过还多了一个 concurrencyLevel 属性,三属性默认值分别为 16、0.75 及 16。其内部使用锁分段技术,维持这锁
Segment的数组,在Segment数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。
put操作:并没有在此方法上加上synchronized,首先对key.hashcode进行hash操作,得到key的hash值。
hash操作的算法和map也不同,根据此hash值计算并获取其对应的数组中的Segment对象(继承自ReentrantLock),
接着调用此Segment对象的put方法来完成当前操作。
ConcurrentHashMap 基于 concurrencyLevel 划分出了多个 Segment 来对 key-value 进行存储,从而避免每
次put操作都得锁住整个数组。在默认的情况下,最佳情况下可允许16个线程并发无阻塞的操作集合对象,尽可能地
减少并发时的阻塞现象。
get(key)
首先对 key.hashCode 进行 hash 操作,基于其值找到对应的 Segment 对象,调用其 get 方法完成当前操
作。而 Segment 的 get 操作首先通过 hash 值和对象数组大小减 1 的值进行按位与操作来获取数组上对应位置的
HashEntry。在这个步骤中,可能会因为对象数组大小的改变,以及数组上对应位置的HashEntry产生不一致性,那
么ConcurrentHashMap是如何保证的?
对象数组大小的改变只有在 put 操作时有可能发生,由于 HashEntry 对象数组对应的变量是 volatile 类型
的,因此可以保证如HashEntry对象数组大小发生改变,读操作可看到最新的对象数组大小。
在获取到了 HashEntry 对象后,怎么能保证它及其 next 属性构成的链表上的对象不会改变呢?这点
ConcurrentHashMap采用了一个简单的方式,即HashEntry对象中的hash、key、next属性都是final的,这也就
意味着没办法插入一个HashEntry对象到基于next属性构成的链表中间或末尾。这样就可以保证当获取到HashEntry
对象后,其基于next属性构建的链表是不会发生变化的。
ConcurrentHashMap 默认情况下采用将数据分为 16 个段进行存储,并且 16 个段分别持有各自不同的锁
感恩于心,回报于行。
Segment,锁仅用于 put 和 remove 等改变集合对象的操作,基于 volatile 及 HashEntry 链表的不变性实现了读取
的不加锁。这些方式使得ConcurrentHashMap能够保持极好的并发支持,尤其是对于读远比插入和删除频繁的Map
而言,而它采用的这些方法也可谓是对于Java内存模型、并发机制深刻掌握的体现。 

您需要登录后才可以回帖 登录 | 立即注册

java面试题网www.wuliaokankan.cnjava建站系统提供技术支持V2.1 网站地图 © 2016-2018