Java 中的线程安全容器

发布时间:2019-11-17 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Java 中的线程安全容器脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

一、同步容器

常用的一些容器例如 ArrayList、HashMap、都不是线程安全的,最简单的将这些容器变为线程安全的方式,是给这些容器所有的方法都加上 synchronized 关键字。

Java 的 Collections 中实现了这些同步容器:

Java 中的线程安全容器

简单的使用如下:

List<String> list = Collections.synchronizedList(new ArrayList<>());  Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());  Set<String> set = Collections.synchronizedSet(new HashSet<>());

同步容器虽然简单,但是相应的效率较低,因为锁的粒度较大

循环遍历同步容器

如果在遍历同步容器的时候,组合了多个方法,这会可能会存在竞态条件,仍然不是线程安全的。解决的办法便是对容器加锁。例如下面这样:

public static void main(String[] args) {     List<String> list = Collections.synchronizedList(new ArrayList<>());          //省略添加数据的操作          String[] str = new String[list.size()];     int k = 0;     synchronized (list){         Iterator<String> iterator = list.iterator();         while (iterator.hasNext()){             str[k ++] = iterator.next();         }     } }

二、并发容器

Java 中还提供了一系列并发容器,相比于同步容器,其性能更好。并发容器共分为了四类:List、Map、Set、Queue。

1. List

List 中一个最主要的实现类是 CopyOnWriteArrayList ,CopyOnWrite,即写时复制,这样的好处是读操作是无锁的。

实现原理是内部维护了一个数组,内部变量 array 指向了这个数组。需要写时,并不是在原数组上操作,而是将数组复制一份,在拷贝的数组中进行写。完成后,将 array 指向新的数组。这样一来,读写之间不互斥,效率得到了很大的提升

需要注意的是 CopyOnWriteArrayList 适用于读多写少的场景,并且需要接受读写的暂时不一致,因为在写的时候,并行的读操作可能并不能马上看到写的结果。

2. Map

Map 的两个主要实现类是 ConcurrentHashMapConcurrentSkipListMap,两者主要的区别是:前者是无序的,后者是有序的。

2.1 ConcurrentHashMap

在 Java 1.7 中,ConcurrentHashMap 的实现使用的是分段锁技,其内部主要的数据结构是 Segment 和 HashEntry,ConcurrentHashMap 包含了一个 Segment 数组,每个 Segment 又包含一个 HashEntry 数组,每个 HashEntry 是一个存储数据的链表结构。

其中 Segment 继承了 ReentrantLock,每个 Segment 都有对应的锁,需要修改数据的时候,需要获取这把锁。修改不同的 Segment 数据,则完全可以并行,效率得到了提升。示意图如下:

Java 中的线程安全容器

Java 1.8 又对 ConcurrentHashMap 做了较大的改进,放弃了分段锁的技术。结构和 Java 1.8 中的 HashMap 类似,采用的是数组+链表/红黑树来实现。为了降低哈希冲突的成本,在链表长度超过 8 时,将链表转换为红黑树。使用 CAS 和 synchronized 解决并发问题,锁住链表或者红黑树的头节点,只要没有哈希冲突,则不会出现并发问题。示意图如下:

Java 中的线程安全容器

2.2 ConcurrentSkipListMap

ConcurrentSkipListMap 保证有序的主要原因是,底层使用的是跳表这种数据结构,关于跳表的介绍,你可以查看数据结构中的内容。

3. Set

Set 的两个实现是 CopyOnWriteArraySetConcurrentSkipListSet

和前面说到的 CopyOnWriteArrayList 、ConcurrentSkipListMap 实现的原理类似。

4. Queue

队列可以从两方面进行分类:

  • 单端和双端:单端队列指的是只能在队首出队,队尾出队,而双端队列指的是队首和队尾均可入队和出队。
  • 阻塞和非阻塞:阻塞队列指的是,当队列满的时候,入队列阻塞;当队列空的时候,出队列阻塞。
  • @H_126_156@

    Java 中,单端队列使用 queue 标识,双端队列使用 deque 标识。

    4.1 单端阻塞队列

    常用的实现类有:ArrayBlockingQueueLinkedBlockingQueuePRiorityQueue

    4.2 单端非阻塞队列

    其实现类是 ConcurrentLinkedQueue

    4.3 双端阻塞队列

    其实现类是 LinkedBlockingDeque

    4.4 双端非阻塞队列

    其实现类是 ConcurrentLinkedDeque

    使用其实都非常地简单,就是入队出队之类的操作,这里不再赘述了。

    脚本宝典总结

    以上是脚本宝典为你收集整理的Java 中的线程安全容器全部内容,希望文章能够帮你解决Java 中的线程安全容器所遇到的问题。

    如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

    本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
    如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。