并发容器详解

并发容器概览

  • ConcurrentHashMap:线程安全的HashMap
  • CopyOnWriteArrayList:线程安全的List
  • BlockingQueue:这是一个接口,表示阻塞队列,非常适合用于作为数据共享的通道
  • ConcurrentLinkedQueue:高效的非阻塞并发队列,使用链表实现,可以看做一个线程安全的LinkedList
  • ConcurrentSkipListMap:是一个Map,使用跳表的数据结构进行快速查找。

古老和过时的同步容器

  • Vector和Hashtable:可以理解为各方法都加了使用synchronized修饰的ArrayList和HashMap。
  • HashMap和ArrayList:使用Collections.synchronizedList(new ArrayList<E>())和Collections.synchronizedMap(new HashMap<E>())进行包装
  • 取代同步的HashMap和同步的ArrayList:ConcurrentHashMap和CopyOnWriteArrayList。

ConcurrentHashMap(重点、面试常考)

  • Map简介
    在这里插入图片描述

  • 为什么需要ConcurrentHashMap

    • 为什么不用Collections.synchronizedMap():相当于对每个方法进行synchronized包装,但synchronized方法在并发量高的时候性能并不理想。
  • 为什么HashMap是线程不安全的

    • 同时put碰撞导致数据丢失
    • 同时put扩容导致数据丢失
    • 死循环造成的CPU100%(兴趣了解)
  • HashMap的分析

    • 1.7的HashMap采用的是数组加链表;1.8采用的是链表加数组(红黑树),当链表的长度超过8之后使用红黑树
      • 红黑树:对二叉查找树BST的一种平衡策略,会自动平衡,防止极端不平衡从而影响查找效率的情况发生。
    • HashMap关于并发的特点:非线程安全;迭代时不允许修改内容;只读的并发是安全的;必要的情况下可以用Collections.synchronizedMap(new HashMap())将HashMap使用到并发环境中。
  • JDK1.7的ConcurrentHashMap
    在这里插入图片描述

    • 1.7中的ConcurrentHashMap最外层是多个segment,每个segment的底层数据结构与HashMap类似,仍然是数组和链表组成的拉链法
    • 每个segment独立上ReentrantLock锁,每个segment之间互不影响,提高了并发效率。
    • ConcurrentHashMap默认有16个Segments,所以最多可以同时支持16个线程并发写(操作分别分布在不同的Segment上)。这个默认值可以在初始化的时候设置为其他值,但是一旦初始化以后,是不可以扩容的。
  • JDK1.8的ConcurrentHashMap

    • 底层与1,8的HashMap类似,都是数组+链表/红黑树,但每一个数组的节点都是同步的。
    • put
      • 判断key value不为空
      • 计算hash值
      • 根据对应位置节点的类型,来赋值,或者helpTransfer,或者增长链表,或者给红黑树增加节点
      • 检查满足阈值就“红黑树化”
      • 返回oldVal
    • get
      • 计算hash值
      • 找到对应的位置,根据情况进行:直接取值;红黑树里找值;遍历链表取值;
      • 返回找到的结果。
  • 为什么要修改结构

    • 数据结构
      由原来的默认16个线程独立变成了每一个数组节点都是独立的,提高的并行度
    • Hash碰撞
      • 红黑树比单纯的拉链法效率更高。
    • 保证并发安全
      • 1.7采用分段锁segment,继承自ReentrantLock;1.8采用CAS + Synchronized
    • 查询复杂度
      • 1.7复杂度O(n),1.8复杂度O(log n)
  • 为什么ConcurrentHashMap也不是线程安全的
    ConcurrentHashMap中有Map中的put方法,而这个方法不是线程安全的;替代的方法是replace方法,replace方法实现线程安全的原理是对变量进行原子更新

CopyOnWriteArrayList

  • 代替Vector和SynchronizedList就和ConcurrentHashMap代替SynchronizedMap的原因一样,Vector和SynchronizedList的锁粒度太大,并发效率相对较低,并且迭代时无法编辑。

  • 适用场景:

    • 读操作可以尽可能的快,写操作慢一点没有太大关系
    • 读多写少:黑名单,每日更新;监听器:迭代操作远多于修改操作。
  • 读写规则:

    • 读写锁:读读共享、其他都互斥
    • CopyOnWriteArrayList相当于读写锁规则升级:读取是完全不用加锁的,写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。
  • CopyOnWriteArrayList实现原理

    • CopyOnWrite的含义:在要对一块内存进行修改时,不直接修改这个内存的值,而是将这个内存中的东西copy一份做修改,然后把之前的指针指到新的内存。
    • 创建新副本、读写分离
    • 旧内存的值是不可变的,所以支持同步读。
    • 迭代的时候用的是旧的地址的值,即使迭代过程中数组发生改变,迭代器也是原来的那个。数据可能过时。
  • CopyOnWriteArrayList缺点

    • 数据一致性问题:CopyOnWrite容器只能保证数据最终一致性,不能保证数据的实时一致性。如果希望写入的数据马上能读到,就不要使用CopyOnWrite容器。
    • 内存占用问题:因为底层采用了复制机制,所以在写的时候,内存中会同时驻扎两个对象的内存。

并发队列Queue(阻塞队列、非阻塞队列)

  • 为什么要使用队列

    • 用队列可以在线程间传递数据:生产者消费者模式
    • 线程安全问题由队列来考虑,降低编码难度
  • 各并发队列关系图
    在这里插入图片描述

  • BlockingQueue阻塞队列

    • 什么是阻塞队列
      • 阻塞队列是具有阻塞功能的队列。通常阻塞队列的一端是给生产者放数据用,另一端给消费者拿数据用。阻塞队列是线程安全的,所以生产者和消费者都可以是多线程的
      • 阻塞功能:最有特色的两个带有阻塞功能的方法
        • take方法:获取并溢出队列头结点,如果执行take方法的时候,队列里无数据则阻塞,直到队列中有数据。
        • put方法:插入数据,如果队列已满就阻塞,直到队列里有空闲空间。
      • 是否有界:可以通过参数控制队列的容量,无界为Integer.MAX_VALUE。
      • 阻塞队列和线程池的关系:阻塞队列时线程池的重要组成部分
    • 主要方法
      • put,take
      • add,remove,element
      • offer,poll,peek
    • ArrayBlockingQueue
      • 有界,可指定容量
      • 公平:可以通过指定是否保证公平。公平则优先处理等待时间最长的线程。这可能会带来一定的性能损耗。
    • LinkedBlockingQueue
      • 无界,容量Integer.MAX_VALUE
      • 内部结构:Node、两把锁
    • PriorityBlockingQueue
      • 支持优先级,自然顺序,不是先进先出。
      • 无界队列
      • PriorityQueue的线程安全版本
    • SynchronousQueue
      • 容量为0,因为SynchronousQueue不需要持有元素,他所做的就是直接传递(direct handoff),所以效率很高。
      • SynchronousQueue没有peek函数,因为容量为0。同理没有iterate相关方法。
      • 是一个极好的用来直接传递的并发数据结构。
      • Executors.newCashedThreadPool()使用的阻塞队列
    • DelayQueue
      • 延迟队列,根据延迟时间排序。元素需要实现Delayed接口,规定排序规则
  • 非阻塞队列ConcurrentLinkedQueue

    • 使用链表作为数据结构,使用CAS非阻塞算法来实现线程安全
  • 如何选择适合的队列

    • 边界
    • 空间
    • 吞吐量

各并发容器总结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值