常用的容器有list、queue、set、map
list有ArrayList、LinkedList,还有一个CopyOnWriteArrayList;
queue有LinkedList、ArrayQueue、LinkedBlockingQueue、ConcurrentLinkedQueue;
map有HashMap、TreeMap、ConcurrentHashMap、ConcurrentSkipListMap;
set内部一般会使用map做存储,有HashSet、TreeSet、CopyOnWriteArraySet、ConcurrentSkipListSet;
之前介绍过ConcurrentHashMap,该map由于需要resize,所以需要锁机制,且加锁粒度较大;Blocking系列由于需要wait、notify操作,所以也需要锁机制;ConcurrentLinkedQueue则是完全的CAS操作。
List
ArrayList是一个数组,在插入元素可能涉及扩容、元素移动,在删除时可能涉及元素移动,如果只是在list尾部插入删除,就不会涉及元素移动,其优势在于随机访问。
LinkedList是一个链表,每次插入元素都会新建一个该元素的节点,然后放入链表中,与ArrayList相比,优势就在于插入、删除操作不会涉及元素移动,只涉及前后引用的改变,同样也不存在扩容问题,链表理论可以无限大(实际长度不能超过Integer.MAX_VALUE,且受限于jvm堆空间)。
CopyOnWriteArrayList是一个数组,与ArrayList不同之处在于,CopyOnWriteArrayList在更新操作前都会新建一个数组(该数组大小正好容下新旧元素),然后在新数组进行更新操作,最后再将旧数组替换掉,并且整个更新过程都是加锁的(ReentrantLock),即是线程安全的。可见,CopyOnWriteArrayList的更新操作的性能不是一般的差啊,但是其优势在于iterator是完全安全的,因为更新操作永远是在一个新数组上进行,而iterator操作永远都是旧数组,但也因为iterator操作的是旧数组,所以CopyOnWriteArrayList的iterator不提供更新操作(如remove)。
Queue
ArrayQueue比ArrayList要高效一些,因为其将更新元素操作限制在了数组的头尾,在插入删除时不再需要移动元素(插入时仍然会涉及扩容)。
LinkedList不仅实现了list接口,同样还实现了queue、deque(double ended queue)接口。
LinkedBlockingQueue提供了一个阻塞的双向队列,可以实现在队列为空、为满时等待,非空、非满时唤醒,这是通过ReentrantLock+Condition实现的,也就是说LinkedBlockingQueue是线程安全的(虽然LinkedBlockingQueue为链式结构,但其在构造器中提供了capacity的设置,默认capacity为Integer.MAX_VALUE)。
ConcurrentLinkedQueue提供了线程安全的队列实现,与LinkedBlockingQueue不同的是ConcurrentLinkedQueue并没有使用锁机制,使用的是CAS机制,这样可以极大提高并发性。
Map
HashMap的hash表是一个数组,解决hash冲突的方式是链表或红黑树(jdk8)。
TreeMap是一棵红黑树。
ConcurrentHashMap结构与HashMap类似,通过在桶上加锁实现了线程安全。
ConcurrentSkipListMap使用了跳表,跳表的物理结构为多层链表,逻辑结构是一棵树,与ConcurrentHashMap相比,ConcurrentSkipListMap的最大特点就是key有序。
Set
CopyOnWriteArraySet包装了CopyOnWriteArrayList,CopyOnWriteArraySet的add方法就是CopyOnWriteArrayList的addIfAbsent方法,同样是线程安全的。