并发容器

并发容器

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

在这里插入图片描述

  • 为什么HashMap是线程不安全的?
    1. 同时put碰撞导致数据丢失。
    2. 同时put扩容导致数据丢失。
    3. 死循环造成CPU100%(JDK7以及以前会出现,原理:在多线程同时扩容的时候,产生循环链表,导致CPU100%)。

1.7里的HashMap结构

在这里插入图片描述

在这里插入图片描述

1.8里HashMap的结构

在这里插入图片描述

在这里插入图片描述
ConcurrentHashMap和CopyOnWriteArrayList取代同步的HashMap和同步的ArrayList。绝大多数情况下ConCurrenthashMap和CopyOnWriteArrayList性能更好。
HashMap只读是安全的,如果并发环境非要用HashMap的话,就用Collections.synchronizedMap(new HashMap())

JDK1.7的ConcurrentHashMap实现与分析

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

在这里插入图片描述
在这里插入图片描述

JDK1.8的ConcurrentHashMap

  • 结构和1.8的HashMap差不多
  • 当冲突达到8的时候,链表会转为红黑树。源码注释里面有说明(出现8的概率非常小,转为红黑树的目的就是处理这种极端情况下查询慢的问题,因为链表短的时候,对速度影响还可以接受)。
  • 为什么达到8才转,不直接使用红黑树,是因为,红黑树的每个节点占用空间是链表的两倍。
  • CAS + synchronized实现线程安全。
* first values are:
     *
     * 0:    0.60653066
     * 1:    0.30326533
     * 2:    0.07581633
     * 3:    0.01263606
     * 4:    0.00157952
     * 5:    0.00015795
     * 6:    0.00001316
     * 7:    0.00000094
     * 8:    0.00000006
     * more: less than 1 in ten million

在这里插入图片描述

  • CopyOnWriteArrayList
    1. 代替Vector和SynchronizedList。
    2. Vector和Synchronized的锁的粒度太大,并发效率相对过低,并且迭代时无法编辑。
    3. Copy-On-Write容器还包括了CopyOnWriteArraySet,用来代替同步Set。
    4. 适用场景:
      • 读操作尽可能的快,而写即使慢一点也没关系。
      • 读多写少:黑名单,每日更新;监听器:迭代操作远多于修改操作。
    5. 读写规则:
      • 同读写锁:读读共享,其他都互斥。
      • 读写锁规则的升级:读取时是完全不用加锁的,而且写入也不会阻塞读操作。只有写入与写入之间需要同步等待。
    6. 实现原理: 创建新的副本、读写分离。
    7. 缺点:
      • 数据一致性问题:CopyOnWrite只能保证数据最终一致性,不能保证实时一致性,如果你希望写入的数据马上被读到,就不要使用CopyOnWrite容器。
      • 内存占用问题:因为CopyOnWrite写是复制机制,所以在进行写的时候,内存里会驻扎两个对象的内存。

在这里插入图片描述

  • 阻塞队列(BlockingQueue):
    1. 阻塞队列是具有阻塞功能的队列。
    2. 通常阻塞队列的一端是给生产者放数据用,另一端给消费者拿数据用。阻塞队列是线性安全的,所以生产者消费者都可以是多线程。
    3. 最具有特色的两个具有阻塞功能的方法:
      • take():获取并移除队列的头结点,一旦如果执行take的时候,队列里无数据,则阻塞,直到队列里有数据。
      • put():插入数据,但是如果队列已满,那么无法继续插入,则阻塞,直到队列里有了空闲空间。
    4. 是否有界(容量大小):这是一个非常重要的属性,无界队列意味着里面可以容纳非常多(Integer.MAX_VALUE,约为2的31次方是一个非常大的数,近似任务无限容量)
    5. 阻塞队列是线程池的重要组成部分。
    6. 主要方法:put、take;add、remove、element;offer、poll、peek。

在这里插入图片描述

  • ArrayBlockingQueue:
    1. 有界
    2. 指定容量
    3. 公平:还可以指定是否需要保证公平,如果想保证公平,那么等待了最长时间的线程会被优先执行,不过这会带来一定得性能损耗。
  • PriorityBlockingQueue:
    1. 支持优先级
    2. 自然顺序(而不是先进先出)
    3. 无界队列
    4. PriorityQueue的线程安全版
  • LinkedBlockingQueue:
    1. 无界
    2. 容量:Integer.MAX_VALUE
    3. 内部结构:node、两把锁
  • SynchronousQueue:
    1. 容量0
    2. 需要注意的是SynchronousQueue的容量不是1而是0,因为SynchronousQueue不需要持有元素,它所有做的是直接传递。
    3. 效率很高。
    4. 是一个极好的用来直接传递的并发数据结构。
    5. SynchronousQueue是线程池Executors.newCachedThreadPool()使用的阻塞队列。

在这里插入图片描述

  • DelayQueue:

    1. 延迟队列,根据延迟时间排序。
    2. 元素需要实现Delayed接口,规定排序规则。
  • 非阻塞并发队列:

    • 并发包中非阻塞并发队列只有ConcurrentLinkedQueue这一种,顾名思义ConcurrentLinkedQueue是使用链表作为其数据结构的,使用CAS非阻塞算法来实现线程安全(不具备阻塞功能),适合用在性能要求较高的并发场景。用的相对较少。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值