JDK源码阅读(九):JUC之并发集合


JUC包中的并发集合主要分为非阻塞队列和阻塞队列

非阻塞队列

非阻塞队列的特色就是队列里面没有数据时,操作队列出现异常或返回null,不具有等待/阻塞的特色。

免锁容器背后的通用策略是:对容器的修改可以与读取操作同时发生,只要读取者只能看到完成修改的结果即可。修改是在容器数据结构的某个部分的一个单独副本(有时是整个数据结构的副本)上执行的,并且这个副本在修改过程中是不可视的。只有当修改完成时,被修改的结构才会自动地与主数据结构进行交换,之后读取这就可以看到这个修改了。

ConcurrentHashMap

ConcurrentHashMap类是支持并发操作的Map对象。
与HashTable的区别:HashTable不支持在循环中remove()元素,HashTable在获得Iterator对象后,不允许更改其结构,否则出现java.util.ConcurrentModificationException异常。

实现原理

ConcurrentHashMap是Java5中新增加的一个线程安全的Map集合,可以用来替代HashTable。通过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,安全的背后是巨大的浪费。那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap的结构

ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色,每个 Segment 对象守护整个散列映射表的若干个桶。每个桶是由若干个 HashEntry 对象链接起来的链表。一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组。ConcurrentHashMap的类图如下所示:

在这里插入图片描述

在这里插入图片描述

这是java8之前ConcurrentHashMap的实现原理,每个Segement其实就相当于一个小的Hashtable,在java8中,对ConcurrentHashMap的结构进行了更新,锁的粒度更加小

改进一:取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。

改进二:将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。

ConcurrentSkipListMap

类ConcurrentSkipListMap支持根据对象的compareTo()方法进行排序

ConcurrentSkipListSet

类ConcurrentSkipListSet支持排序而且不允许重复的元素

ConcurrentLinkedQueue

类ConcurrentLinkedQueue提供了并发环境的队列操作。

  • 方法poll()当没有获得数据时返回null,如果有数据时则移除表头,并将表头进行返回。
  • 方法element()当没有获得数据时出现NoSuchElementException异常,如果有数据时则返回表头项。
  • 方法peek()当没有获得数据时返回为null,如果有数据时则不移除表头,并将表头进行返回。

ConcurrentLinkedDeque

ConcurrentLinkedQueue仅支持对列头进行操作,而ConcurrentLinkedDeque支持对列头列尾双向操作。

CopyOnWriteArrayList

线程安全的ArrayList,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得赋值的数组在被修改时,读取操作可以安全的执行,当修改完成时,一个原子性的操作将把新的数组换入,使得新的读取操作可以看到这个新的修改。

CopyOnWriteArraySet

线程安全的HashSet

阻塞队列

  • 如果阻塞队列是空的,从阻塞队列中取东西的操作将会被阻塞进入等待状态,直到阻塞队列中添加了元素才会被唤醒。
  • 如果阻塞队列是满的,从阻塞队列中存放元素的操作将会进行等待状态,知道阻塞队列里有剩余空间才会被唤醒继续操作。

ArrayBlockingQueue

类ArrayBlockingQueue提供一种有界阻塞队列的功能。底层使用数组实现。

LinkedBlockingQueue

类LinkedBlockingQueue和ArrayBlockingQueue在功能上大体一样,只不过ArrayBlockingQueue是有界的,而LinkedBlockingQueue是无界的,但也可以定义为有界的。底层使用链表实现。

从LinkedBlockingQueue的几个常用方法讲解它是如何实现阻塞的

offer()

//采用AtomicInteger保存队列中的元素个数
private final AtomicInteger count = new AtomicInteger(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值