并发编程(十五):并发容器

1.同步容器

同步容器,指的是通过synchronized关键字来保证原子性,类似如下的包装类都是基于此考量:

ArrayList、HashSet 和 HashMap 包装成了线程安全的 List、Set 和 Map。

List list = Collections.
  synchronizedList(new ArrayList());
Set set = Collections.
  synchronizedSet(new HashSet());
Map map = Collections.
  synchronizedMap(new HashMap());

但是针对包装后的容器采用迭代器遍历,还是会存在原子性的问题,因此这种场景也可以进行synchronized进行包装

List list = Collections.
  synchronizedList(new ArrayList());
synchronized (list) {  
  Iterator i = list.iterator(); 
  while (i.hasNext())
    foo(i.next());
}    

Java 提供的同步容器还有 Vector、Stack 和 Hashtable,这三个容器不是基于包装类实现的,但同样是基于 synchronized 实现的,对这三个容器的遍历,同样要加锁保证互斥

2.并发容器

Java1.5之前都是通过同步容器实现线程安全,但是使用synchronized串行度太大了,性能太差。JDK1.5之后提供了性能更高的容器,称之为并发容器

image-20210816170617616

1.1.List

List只有一个实现类:CopyOnWriteArrayList

其采取的机制其实就是写时复制(CopyOnWrite)机制,读操作的时候不进行处理,写操作的时候复制数组并在复制的数组执行写操作,最后将数据的引用指向新的数组

image-20210816172935851 image-20210816172944273

需要注意的几个地方:

  1. 数据会存在短暂的不一致,写入的新元素无法立即被读取到
  2. CopyOnWriteArrayList 迭代器是只读的,不支持增删改。因为迭代器遍历的仅仅是一个快照,而对快照进行增删改是没有意义的

1.2.Map

Map接口的两个实现主要是:ConcurrentHashMap、ConcurrentSkipListMap。它们从应用的角度来看,主要区别在于ConcurrentHashMap 的 key 是无序的,而 ConcurrentSkipListMap 的 key 是有序的

image-20210816174536939

1.3.Set

Set 接口的两个实现是 CopyOnWriteArraySet 和 ConcurrentSkipListSet,使用场景可以参考前面讲述的 CopyOnWriteArrayList 和 ConcurrentSkipListMap

1.4.Queue

可以按照两个维度针对队列进行分类:

  1. 阻塞或非阻塞,所谓阻塞是指队满时,入队操作阻塞。队空时,出队操作阻塞。阻塞队列都用 Blocking 关键字标识
  2. 单端或双端队列,单端是只允许一段出队或入队,双端是允许双端进行入队出队操作,单端队列使用 Queue 标识,双端队列使用 Deque 标识

此时可将Queue分为如下四大类:

  1. 单端阻塞队列:其包括ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、LinkedTransferQueue、PriorityBlockingQueue 和 DelayQueue。内部一般会持有一个队列结构,其实现包括数组,例如ArrayBlockQueue,或链表,例如LinkedBlockQueue。甚至还可以不持有队列,例如SynchronousQueue,此时生产者的入队操作必须等待消费者的出队操作。而 LinkedTransferQueue 融合 LinkedBlockingQueue 和 SynchronousQueue 的功能,性能比 LinkedBlockingQueue 更好。PriorityBlockQueue支持按照优先级出队(其实现是一个二叉堆),DelayQueue支持延时出队。
  2. 双端阻塞队列:其包括LinkedBlockDeque
  3. 单端非阻塞队列:其包括ConcurrentLinkedQueue
  4. 双端非阻塞队列:其实现是 ConcurrentLinkedDeque

只有 ArrayBlockingQueue 和 LinkedBlockingQueue 是支持有界的,所以在使用其他无界队列时,一定要充分考虑是否存在导致 OOM 的隐患

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×