Java并发容器及使用场景

针对多线程并发设计,使用了锁分段技术,只对操作的位置进行同步操作,减小锁的粒度,从而提高吞吐量。

Posted by guanyang on 2022-08-03
Words 1k and Reading Time 3 Minutes
Viewed Times

背景

  • Java集合容器,主要有四大类别:List Set Queue Map,常见的ArrayList HashMap这些都不是线程安全的
  • 同步容器:简单理解为通过synchronized来实现同步的容器,比如VectorHashtable以及SynchronizedList等容器
  • 同步容器由于共同竞争容器级别的锁,虽然解决了线程安全问题,但是整体吞吐量降低

并发容器设计思路

  • 针对多线程并发设计,使用了锁分段技术,只对操作的位置进行同步操作,减小锁的粒度,从而提高吞吐量
  • 采用CAS算法和部分代码使用synchronized锁保证线程安全

并发容器分类

List

  • CopyOnWriteArrayList
    • 目标:代替Vector、synchronizedList
    • 原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。

Map

  • ConcurrentHashMap
    • 目标:代替Hashtable、synchronizedMap,支持复合操作
    • 原理:JDK6中采用一种更加细粒度的加锁机制Segment“分段锁”,JDK8中采用CAS无锁算法。
  • ConcurrentSkipListMap
    • 目标:代替synchronizedSortedMap(TreeMap)
    • 原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。

Set

  • ConcurrentSkipListSet
    • 目标:代替synchronizedSortedSet
    • 原理:内部基于ConcurrentSkipListMap实现
  • CopyOnWriteArraySet
    • 目标:代替synchronizedSet
    • 原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。

BlockingQueue

  • ArrayBlockingQueue
    • 基于数组实现的阻塞FIFO队列
    • 通过ReentrantLock实现线程安全,由notEmptynotFull两个Condition实现阻塞和唤醒
  • LinkedBlockingQueue
    • 基于链表实现的阻塞FIFO队列
    • 通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒,由takeLockputLock两把锁保证并发安全
  • SynchronousQueue
    • 同步队列,可用于线程间交换数据却不用存储数据
    • 队列支持公平和非公平的模式,公平模式的数据结构是队列(FIFO),非公平模式使用的是栈(LIFO)
  • LinkedTransferQueue
    • 基于链表实现的无界阻塞队列
    • 相当于LinkedBlockingQueueSynchronousQueue的合体,性能比LinkedBlockingQueue更高,比SynchronousQueue能存储更多的元素
    • put时,如果有等待的线程,就直接将元素 “交给” 等待者, 否则直接进入队列
    • puttransfer方法的区别是,put是立即返回的,transfer是阻塞等待消费者拿到数据才返回。
  • PriorityBlockingQueue
    • 基于数组实现的支持优先级的无界阻塞队列,底层是基于二叉小顶堆实现
    • 通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒
  • DelayQueue
    • 支持延时获取元素的无界阻塞队列,内部使用PriorityQueue来实现
    • 进入队列的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素,只有在延迟期满时才能从中提取元素

BlockingDeque

  • LinkedBlockingDeque
    • 基于双向链表实现的阻塞FIFO或FILO队列
    • 通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒

ConcurrentLinkedQueue

  • 并发中的非阻塞队列,采用CAS非阻塞算法实现线程安全
  • 基于链表实现的FIFO队列(LinkedList的并发版本)
  • 由于使用CAS没有使用锁,所以获取size的时候有可能进行offer,poll或者remove操作,导致获取的元素个数不精确,所以在并发情况下size函数不是很有用。

ConcurrentLinkedDeque

  • 并发中的非阻塞队列,采用CAS非阻塞算法实现线程安全
  • 基于双向链表实现的FIFO或FILO队列
  • 由于使用CAS没有使用锁,所以获取size的时候有可能进行offer,poll或者remove操作,导致获取的元素个数不精确,所以在并发情况下size函数不是很有用。

If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !

...

...

00:00
00:00