《实战 Java 高并发程序设计》笔记——第3章 JDK 并发包(三)

本文介绍了 Java 高并发编程中 JDK 提供的并发容器,包括 ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue 和 BlockingQueue。强调了这些线程安全的容器在多线程环境中的高效性和使用场景,如 ConcurrentHashMap 的并发优化,CopyOnWriteArrayList 的读多写少优势,以及 BlockingQueue 作为数据共享通道的作用。文章还探讨了并发容器的内部实现,如并发HashMap的线程安全包装和跳表(SkipList)的数据结构及其在并发查找中的优势。
摘要由CSDN通过智能技术生成

声明:

本博客是本人在学习《实战 Java 高并发程序设计》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。

本博客已标明出处,如有侵权请告知,马上删除。

3.3 不要重复发明轮子:JDK 的并发容器

除了提供诸如同步控制,线程池等基本工具外,为了提高开发人员的效率,JDK 还为大家准备了一大批好用的容器类,可以大大减少开发工作量。大家应该都听说过一种说法,所谓程序就是 “算法+数据结构”,这些容器类就是为大家准备好的线程数据结构。你可以在里面找到链表、HashMap、队列等。当然,它们都是线程安全的。

在这里,我也打算花一些篇幅为大家介绍一下这些工具类。这些容器类的封装都是非常完善并且 “平易近人” 的,也就是说只要你有那么一点点的编程经验,就可以非常容易地使用这些容器。因此,我可能会花更多的时间来分析这些工具的具体实现,希望起到抛砖引玉的作用。

3.3.1 超好用的工具类:并发集合简介

JDK 提供的这些容器大部分在 java.util.concurrent 包中。我先提纲挈领地介绍一下它们,初次露脸,大家只需要知道他们的作用即可。有关具体的实现和注意事项,在后面我会慢慢道来。

  • ConcurrentHashMap:这是一个高效的并发 HashMap。你可以理解为一个线程安全的 HashMap。
  • CopyOnWriteArrayList:这是一个 List,从名字看就是和 ArrayList 是一族的。在读多写少的场合,这个 List 的性能非常好,远远好于 Vector。
  • ConcurrentLinkedQueue:高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList。
  • BlockingQueue:这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。
  • ConcurrentSkipListMap:跳表的实现。这是一个Map,使用跳表的数据结构进行快速查找。

除了以上并发包中的专有数据结构外,java.util 下的 Vector 是线程安全的(虽然性能和上述专用工具没得比),另外 Collections 工具类可以帮助我们将任意集合包装成线程安全的集合。

3.3.2 线程安全的 HashMap

在之前的章节中,已经给大家展示了在多线程环境中使用 HashMap 所带来的问题。那如果需要一个线程安全的 HashMap 应该怎么做呢

一种可行的方法是使用 Collections.synchronizedMap() 方法包装我们的 HashMap。如下代码,产生的 HashMap 就是线程安全的:

在这里插入图片描述

Collections.synchronizedMap() 会生成一个名为 SynchronizedMap的Map。它使用委托,将自己所有 Map 相关的功能交给传入的 HashMap 实现,而自己则主要负责保证线程安全。

具体参考下面的实现,首先 SynchronizedMap 内包装了一个 Map。

在这里插入图片描述

通过 mutex 实现对这个 m 的互斥操作。比如,对于 Map.get() 方法,它的实现如下:

在这里插入图片描述

而其他所有相关的 Map 操作都会使用这个 mutex 进行同步。从而实现线程安全。

这个包装的 Map 可以满足线程安全的要求。但是,它在多线程环境中的性能表现并不算太好。无论是对 Map 的读取或者写入,都需要获得 mutex 的锁,这会导致所有对 Map 的操作全部进入等待状态,直到 mutex 锁可用。如果并发级别不高,一般也够用。但是,在高并发环境中,我们也有必要寻求新的解决方案。

一个更加专业的并发 HashMap 是 ConcurrentHashMap。它位于 java.util.concurrent 包内。它专门为并发进行了性能优化,因此,更加适合多线程的场合

有关 ConcurrentHashMap 的具体实现细节,大家可以参考 “第4章 锁的优化及注意事项” 一章。我们将在那里给出更加详细的实现说明。

3.3.3 有关 List 的线程安全

队列、链表之类的数据结构也是极其常用的,几乎所有的应用程序都会与之相关。在 Java 中,ArrayList 和 Vector 都是使用数组作为其内部实现。两者最大的不同在于 Vector 是线程安全的,而 ArrayList 不是。此外,LinkedList 使用链表的数据结构实现了 List。但是很不幸,LinkedList 并不是线程安全的,不过参考前面对 HashMap 的包装,在这里我们也可以使用 Collections.synchronizedList() 方法来包装任意 List,如下所示:

在这里插入图片描述

此时生成的 List 对象就是线程安全的。

3.3.4 高效读写的队列:深度剖析 ConcurrentLinkedQueue

队列 Queue 也是常用的数据结构之一。在 JDK 中提供了一个 ConcurrentLinkedQueue 类用来实现高并发的队列。从名字可以看到,这个队列使用链表作为其数据结构。有关 ConcurrentLinkedQueue 的性能测试,大家可以自行尝试。这里限于篇幅就不再给出性能测试的代码。大家只要知道 ConcurrentLinkedQueue 应该算是在高并发环境中性能最好的队列就可以了。它之所有能有很好的性能,是因为其内部复杂的实现。

在这里,我更加愿意花一些篇幅来简单介绍一下 ConcurrentLinked

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值