一篇文章带你认识 JDK 的并发容器
除了提供诸如同步控制、线程池等基本工具外,为了提高开发人员的效率,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就是线程安全的。
public static Map m=Collections.synchronizedMap(new HashMap());
Collections.synchronizedMap()
方法会生成一个名为SynchronizedMap的Map。它使用委托,将自己所有Map相关的功能交给传入的HashMap实现,而自己则主要负责保证线程安全。
具体参考下面的实现,首先SynchronizedMap
内包装了一个Map。
通过 mutex 实现对这个 m 的互斥操作。比如,对于Map.get()
方法,它的实现如下:
public V get(Object key){
synchronized(mutex){ return m.get(key);}
}
而其他所有相关的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:
public static List<String> l=Collections.synchronizedList(new LinkedList<String>());
此时生成的List对象就是线程安全的了。
还没有评论,来说两句吧...