JAVA面试小结四十九:JAVA并发集合(Concurrent Collections)有哪些?

常用的并发集合有如下几种:

一、ConcurrentHashMap

ConcurrentHashMap 是一个线程安全的 HashMap 实现。 

工作原理

        ConcurrentHashMap在JDK 1.7的实现原理:

在JDK 1.7中,ConcurrentHashMap采用了一种叫做**分段锁(Segmentation Lock)**的技术。它内部包含多个Segment,每个Segment都拥有一个锁。当线程访问ConcurrentHashMap时,会根据哈希算法计算出应该访问哪个Segment,然后只锁定该Segment进行操作,其他线程访问其他Segment则不受影响,从而实现真正的并发访问。每个Segment都维护着一个HashEntry数组,用于存储键值对数据。

        ConcurrentHashMap在JDK 1.8的实现原理:

在JDK 1.8中,ConcurrentHashMap的实现原理发生了较大的变化,它摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用CAS算法和synchronized,使得多个线程可以在不同段上进行并发操作,从而实现在高并发场景下的高性能。当链表长度大于一定阈值(默认为8)时,链表转化为红黑树,进一步减少搜索时间。

使用场景

ConcurrentHashMap适用于需要高并发的场景,尤其是读多写少,或者读写都很多的场景。它的主要优势在于可以支持多个线程并发写入,因此,当我们需要在并发环境中使用Map时,它是很好的选择。例如:

  1. 缓存:在创建缓存系统时,ConcurrentHashMap是一个很好的选择,因为它可以处理高并发的数据访问。
  2. 数据库连接池:数据库连接池通常会使用ConcurrentHashMap来存储和管理连接对象,以支持多线程并发获取或释放数据库连接。
  3. 统计系统:例如网站统计用户的访问量,可以用ConcurrentHashMap记录用户的访问情况,由于是多线程的,可以支持更高的并发。

二、CopyOnWriteArrayList 和 CopyOnWriteArraySet 

CopyOnWriteArrayList 和 CopyOnWriteArraySet 是线程安全的 List 和 Set 实现,它们通过每次修改时复制底层数组来实现线程安全。

工作原理

CopyOnWriteArrayList 
在所有的修改方法执行之前,首先要获取锁
在使用元素添加方法时,每次会拷贝出一个长度 +1 的新数组,同时把要添加的元素放入到新数组的最后一个位置上,最后把旧数组替换成新数组
在使用元素移除方法时,每次会拷贝出一个长度 -1 的新数组,最后根据要删除的元素位置,进行数组拷贝,最后把旧数组替换成新数组
所有读取方法都没有采取任何同步措施,包括获取迭代器
获取迭代器的方法,并没有像很多文章中说的对当前数组进行了一个拷贝,而是直接把当前数组的引用传递给了迭代器
之所以不用对当前数组进行拷贝,是因为每次的修改方法都会创建出一个新数组,所以 CopyOnWriteArrayList 迭代器根本不会,也没有必要去拷贝当前数组

CopyOnWriteArrayList 的主要工作原理就是 "写时复制" (Copy-On-Write)。这意味着所有的修改操作(如:add、set 和 remove)都是在底层的数组的一个副本上进行的。这个原理帮助我们解决了并发环境下的修改冲突问题。

具体来说,当我们调用一个修改操作时,CopyOnWriteArrayList 会首先锁定它的内部锁,然后复制底层的数组,接着在新的数组副本上进行修改,最后再将引用从原来的数组切换到新的数组。由于所有的修改操作都是在数组的副本上进行的,所以读操作完全不需要锁定,可以直接读取。这样就大大提高了并发读的性能。

然而,由于每次修改都需要复制整个数组,所以写操作的性能和内存开销都比较大。尤其是当数组的大小非常大时,写操作的开销就变得更加显著。这也是为什么我们通常只在读操作远多于写操作的场景中使用 CopyOnWriteArrayList。

CopyOnWriteArraySet
CopyOnWriteArraySet 是一个并发安全的无重复集合,底层是基于 CopyOnWriteArrayList 来实现

使用场景

由于 CopyOnWriteArrayList 的特性——读取高效、写入时复制整个底层数组,因此它非常适用于读操作远多于写操作的场景。例如,实时读取系统配置信息的场景。系统配置通常只在系统启动或管理员修改时更新,而在系统运行时,可能会有大量的读取操作,此时使用 CopyOnWriteArrayList 能够提供很高的性能。

再比如,对于事件监听器的管理,我们通常会在应用启动时添加大部分监听器,而在运行过程中,这些监听器的列表很少改动,大部分操作是遍历这个列表来通知所有监听器。这种场景下,CopyOnWriteArrayList 也是非常合适的。

三、BlockingQueue 

BlockingQueue 描述了一个阻塞队列应该有的行为,例如:

  • 阻塞读方法:take()
  • 阻塞写方法:put()

阻塞的读写方法的函数签名上都会抛出 InterruptedException,具体的阻塞逻辑由实现类自行实现。

BlockingQueue 接口及其实现类(如 ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue 等)为并发编程提供了线程安全的队列结构。

ArrayBlockingQueue

是一个基于数组实现的阻塞队列,创建对象时必须指定容量

LinkedBlockingQueue

是一个基于单向链表实现的阻塞队列,其容量为:

如果在构造函数中指定了容量,那么容量就是队列长度的最大边界限制
如果没有在构造函数中指定容量,容量将会默认设置成 Integer.MAX_VALUE,近似无界
所以,LinkedBlockingQueue 在不传入容量的情况下,最大容量会被设置为 Integer.MAX_VALUE

PriorityBlockingQueue 

PriorityBlockingQueue 底层有一个可扩容的对象数组 Object[] queue,结构是堆
内置一个 ReentrantLock 独占锁,以及由这个锁构造出来的 Condition notEmpty 条件
一个 volatile 关键字修饰的整型变量 volatile int allocationSpinLock

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值