一篇文章带你认识 JDK 的并发容器

绝地灬酷狼 2022-12-26 04:44 339阅读 0赞

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

文章目录

    • 一、并发集合简介
    • 二、线程安全的HashMap
    • 三、有关 List 的线程安全

一、并发集合简介

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

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

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

二、线程安全的HashMap

在之前的章节中,已经给大家展示了在多线程环境中使用HashMap所带来的问题,如果需要一个线程安全的HashMap应该怎么做呢?
一种可行的方法是使用Collections.synchronizedMap()方法包装我们的HashMap。如下代码,产生的HashMap就是线程安全的。

  1. public static Map m=Collections.synchronizedMap(new HashMap());

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

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

在这里插入图片描述
通过 mutex 实现对这个 m 的互斥操作。比如,对于Map.get()方法,它的实现如下:

  1. public V get(Object key){
  2. synchronized(mutex){ return m.get(key);}
  3. }

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

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

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

三、有关 List 的线程安全

队列、链表之类的数据结构也是极其常用的,几乎所有的应用程序都会与之相关。在Java中,ArrayList和Vector都使用数组作为其内部实现。两者最大的不同在于Vector是线程安全的,而ArrayList不是

此外,LinkedList使用链表的数据结构实现了List。但是很不幸,LinkedList并不是线程安全的,不过参考前面对HashMap的包装,这里我们也可以使用Collections.synchronizedList()方法来包装任意List:

  1. public static List<String> l=Collections.synchronizedList(new LinkedList<String>());

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

发表评论

表情:
评论列表 (有 0 条评论,339人围观)

还没有评论,来说两句吧...

相关阅读