ArrayList
- ArrayList 是基于动态数组的数据结构,线程不安全。
- 随机访问性能 ArrayList 优于 LinkedList。ArrayList 时间复杂度为 O(1) ,LinkedList 时间复杂度为 O(n)【需要遍历整个链表才能找到指定的元素】
LinkedList
- 基于链表的数据结构,线程不安全。
- 插入和删除性能 LinkedList 优于 ArrayList。 LinkedList 时间复杂度为 O(1),ArrayList 的时间复杂度为 O(n)
Vector
和 ArrayList 一样都是基于数组实现,但线程安全【通过 synchronized 关键字保证】。
- Vector 性能比 ArrayList 差,单线程下 ArrayList 要比 Vector 快
- 扩容时,ArrayList 扩容50%,Vector 扩容 100%
CopyOnWriteArrayList
-
大多数应用场景下读操作的比例远远大于写,所以读的时候没必要加锁,写写场景下加锁互斥即可
-
CopyOnWriteArrayList【写时复制容器】满足上述场景,即:读写不互斥。写入操作时,进行一次自我复制产生一个副本,写操作就在副本中执行,写完之后,再将副本替换原来的数据。这样,在写数据的同时不影响读数据操作。
-
读操作源码
- 从数组中获取指定下标的数据,读不需要加锁,所以只是简单的不加锁方法
-
写操作
- 执行写操作时,先进行 lock 加锁。然后复制原数组创建一个长度加一的新数组【副本数组】,新增操作基于副本数组操作。新增操作完成后,使用副本数组替换旧数组。array 是 volatile 的,保证了多线程之间的可见性。
-
优势
-
读写不互斥、写写互斥,多线程并发安全。读写分离,提高并发吞吐
-
不支持fail-first机制,不抛出 ConcurrentModificationException 异常
当我们使用 iterator 进行遍历时,如果遍历途中使用集合的 add/remove 方法进行修改,此时会抛出 ConcurrentModificationException 异常,产生 fail-fast 事件
CopyOnWriteArrayList 在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发 ConcurrentModificationException
-
-
劣势
- 只保证最终一执性。一条线程在执行修改操作,另一条线程在执行读取操作,读取的线程并不能看到最新的数据。
即使执行 setArray() 将指向改成了新数组,原本读取的线程也不能看到最新的数据。因为读取线程在执行读操作时并不是直接访问成员array完成的,而是通过getArray()方法的形式获取到的数组数据,在getArray()方法执行完成之后,读取数据的线程拿到的引用已经是旧数组的地址了,之后就算修改成员array的指向也不会影响get的访问
- 数据过大时,内存占用高
- 只保证最终一执性。一条线程在执行修改操作,另一条线程在执行读取操作,读取的线程并不能看到最新的数据。