Java 并发容器

并发容器非常多,自己理解把Java的并发容器常用分为如下三类:

分问题整理下自己曾经的疑问。

为什么有这么多并发容器?

这个问题,其实很好回答,场景多么,那一般场景有几个区分维度?维度有下面几个:

其实上述维度并不全,但组合下来算算,java的并发容器种类还好了。

BlockingQueue的feature与场景?

ArrayBlockingQueue VS LinkedBlockingQueue

参考:
disruptor
BlockingQueue in Java

ConcurrentLinkedQueue vs LinkedBlockingQueue

首先,最明显的区别就是LinkedBlockingQueue是个block的queue,它实现了BlockingQueue的interface,比如put和take,这些方法是实现生产者消费者模式的必备的接口。如果ConcurrentLinkedQueue需要实现一个阻塞的take,那么需要用poll来等待,是这样的:

while(result == null)
   result = concurrentLinkedQueue.poll();

而block的用法是这样的:

linkedBlockingQueue.take();

显然后者在一般的情况下是比较高效的。一般常用的模式就是生产者消费者模式了,那ConcurrentlinkedQueue的应用场景是什么呢?
参考:LinkedBlockingQueue vs ConcurrentLinkedQueue一般来说有两种场景:

对于第一种情况,与select和poll模型很像,不要认为select很挫,当IO非常频繁,每次select轮询,绝大多数的socket都有数据的时候,select有可能就比poll高效了。同样一种理想的情况,produce的消息非常快,consume的消息也非常快,而且速度很相近,队列里面有恰好保留那么点数据,这样consume的时候一直有数据,也不至于poll浪费cpu了。

为什么ConcurrentLinkedQueue没有阻塞的put和take方法呢?ConcurrentLinkedQueue是lock-free的结构,如果在加上阻塞的put和take方法,那就不是什么lock-free的了,自然没有了lock-free的优势了。

ConcurrentHashMap的底层实现?为啥能替代Hashtable

优化的技巧

CopyOnWrite的feature与场景?

ps:与ConcurrentHashMap类似,但是感觉应该用在数据相对较少的场景中(查找效率可能比HashMap高),而且数据量较少复制开销小能避免GC问题
PS:另外,需要用在允许实时性较差的场景中,允许读取一定时间的旧数据
ps:为什么设计成写的时候允许读到旧数据?这样设计为了允许更快速的读取(也是这个数据结构优势所在),如果更新的时候不允许读那么与一般的不copy的结构效率上也没啥差
ps:与ConcurrentHashMap区别,感觉一是在数据量大小上,二是在读取上,CopyOnWrite允许在修改过程中疯狂读取

CopyOnWrite造成的问题

为什么没有CopyOnWriteMap和CopyOnWriteLinkedList?

没有CopyOnWriteMap可能因为有了ConcurrentHashMap,也是读远多于写的情况,ConcurrentHashMap已经非常好用了。
为什么没有CopyOnWriteLinkedList没有必要吧,修改LinkedList很快。CopyOnWirte系类的特点的读性能要求高,list的底层结构读效率不如array的。

非线程安全的优势?

就一点,速度快。很多的容器都有自己对应的线程安全版本,如Vector与ArrayList,Hashtable(被ConcurrentHashMap代替)与Hashmap。

PS:HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的
PS:Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上(删除或插入)”更改集合对象,将会抛出ConcurrentModificationException异常。 但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

Table of Contents