一、Collection
集合,继承了Iterable接口
1、Vector:
线程安全,几乎所有方法都是sync,效率较低。
2、ArrayList:
线程不安全,底层用数组实现。
3、Queue:
主要为了实现高并发而出现的接口,继承于Collection,主要提供了add、offer、poll、remove、element、peek等方法声明。
方法介绍:
add:增加一个元索,则抛出一个IIIegaISlabEepeplian异常
offer:添加一个元素并返回true,如果队列已满,则返回false
remove:移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
poll:移除并返问队列头部的元素,如果队列为空,则返回null
element:返回队列头部的元素, 如果队列为空,则抛出一个NoSuchElementException异常
peek:返回队列头部的元素, 如果队列为空,则返回null
4、LinkedList:
线程不安全,底层用链表实现。继承了AbstractSequentialList类,实现了List和Deque接口。
5、CopyOnWriteArrayList:
线程安全,底层用数组实现,实现了List接口
通过写时复制保证线程安全:
① CopyOnWriteArrayList是通过“volatile数组”来保存数据的。一个线程读取volatile数组时,总能看到其它线程对该volatile变量最后的写入。就这样,通过volatile提供了“读取到的数据总是最新的”这个机制的保证。
② CopyOnWriteArrayList通过互斥锁来保护数据。在“添加/修改/删除”数据时,会先“获取互斥锁”,在修改完毕之后,先将数据更新到“volatile数组”中,然后再“释放互斥锁”;这样,就达到了保护数据的目的。
③ 它不存在扩容的概念,每次写操作都要复制一个副本,在副本的基础上修改后改变Array引用。CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能较低差。
④ 读操作和读取ArrayList相同,不加锁。
⑤ 适合使用在List大小较小,且读操作远远大于写操作的场景里,比如缓存。由于无法保证CopyOnWriteArrayList到底要放置了多少数据,故在写操作时可能会引起FGC或发生OOM。
⑥ 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
6、CopyOnWriteArraySet:
线程安全,底层用数组实现,继承了AbstractSet类。
通过写时复制保证线程安全。类似于CopyOnWriteArrayList
7、LinkedBlockingQueue:
阻塞队列,线程安全,底层用链表实现。继承了AbstractQueue,实现了BlockingQueue接口。
通过对操作加ReentrantLock保证线程安全。
通过实现BlockingQueue接口,实现了put和take两个方法,这两个方法实现了阻塞。put往里装,如果容器装满则线程阻塞,take往外取,如果容器为空则线程阻塞。
注:LinkedBlockingQueue是一个无界队列,可以一直添加元素直到内存满了为止。
8、ArrayBlockingQueue:
阻塞队列,线程安全,底层用数组实现。
与LinkedBlockingQueue的区别为:ArrayBlockingQueue是有界的。容量满之后,put会被阻塞,add会抛出异常,offer会失败。
二、Map
1、HashTable:
线程安全,几乎所有方法都是sync,效率较低。
2、HashMap:线程不安全。
在jdk8以前是通过散列表(数组+链表)实现,jdk8开始用数组+链表+红黑树实现(当链表长度超过8时,链表转换为红黑树)。这是由于当Hash冲突严重时,在桶上形成的链表会变的越来越长,这样在查询时的效率就会越来越低(时间复杂度达到 O(N))
。
3、SynchronizedHashMap:
线程安全,通过Collections.SynchronizedMap(Map)获取,实现了Map接口,内部维护着一个map成员变量,所有操作都是加锁后对此map进行操作。
4、ConCurrentHashMap:
线程安全,无序,多线程中最常用的Map,底层通过CAS实现线程安全。
实现原理:
① 在jdk1.8以前,通过散列表(数组+链表)实现。
value、链表都用 volatile 修饰,保证了获取时的可见性。采用分段锁技术,其中Segment继承于 ReentrantLock。每当一个线程占用锁访问一个Segment时,不会影响到其他的Segment,理论上支持Segment数量大小的并发。
② 在jdk1.8后,开始用数组+链表+红黑树实现(当链表长度超过8时,链表转换为红黑树),这是由于当Hash冲突严重时,在桶上形成的链表会变的越来越长,这样在查询时的效率就会越来越低(时间复杂度达到 O(N))
。
value、链表都用 volatile 修饰,保证了获取时的可见性。抛弃了原有的 Segment 分段锁而采用了 CAS + synchronized
来保证并发安全性。
Node是ConcurrentHashMap存储结构的基本单元,继承于HashMap中的Entry,用于存储数据,Node是一个链表,但是只允许对数据进行查找,不允许进行修改。
TreeNode继承与Node,但是数据结构换成了二叉树结构,它是红黑树的数据的存储结构,用于红黑树中存储数据,当链表的节点数大于8时会转换成红黑树的结构,也就是通过TreeNode作为存储结构代替Node来转换成黑红树。
put方法:
1、如果没有初始化就先调用initTable方法来进行初始化过程。
2、如果没有hash冲突就直接CAS插入。
3、如果当前位置的 hashcode == MOVED == -1,
则需要进行扩容。
4、如果存在hash冲突,就加锁(锁链表头结点)来保证线程安全,这里有两种情况,一种是链表形式,就一直遍历直到找到对应的key修改value或从尾端插入新的Node,一种是红黑树就按照红黑树结构修改或插入。
5、如果Hash冲突时会形成Node链表,在链表长度超过8时,会将链表结构转换为红黑树的结构。
6、如果添加成功(通过判断oldValue是否为null)就调用 addCount 方法统计size,并且检查是否需要扩容。
5、ConcurrentSkipListMap:
线程安全,有序,底层用跳表实现,通过CAS保证线程安全。