用Vector替换ArrayList

  1. import java.util.List;
  2. import java.util.Vector;
  3. public class Test {
  4. public static void test() {
  5. List list = new Vector();
  6. }
  7. }

Collections.synchronizedList

  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. import java.util.List;
  4. public class Test {
  5. public static void test() {
  6. List list = new ArrayList();
  7. List synchronizedList = Collections.synchronizedList(list);
  8. }
  9. }

CopyOnWriteArrayList

CopyOnWriteArrayList介绍

①、CopyOnWriteArrayList,写数组的拷贝,支持高效率并发且是线程安全的,读操作无锁的ArrayList。所有可变操作都是通过对底层数组进行一次新的复制来实现。
②、CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。它不存在扩容的概念,每次写操作都要复制一个副本,在副本的基础上修改后改变Array引用。CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差。
③、CopyOnWriteArrayList 适合读多写少的场景,不过这类慎用 ,因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。

CopyOnWriteArrayList 有几个缺点:

1、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc。

1、young gc :年轻代(Young Generation):对象被创建时,内存的分配首先发生在年轻代(大对象可以直接被创建在年老代),大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉(IBM的研究表明,98%的对象都是很快消亡的),这个GC机制被称为Minor GC或叫Young GC。 2、老年代(Old Generation):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时,将执行Major GC,也叫 Full GC

2、不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求

CopyOnWriteArrayList的一些方法

1、add(E e) :将指定元素添加到此列表的尾部,返回值为boolean。
2、add(int index, E element) : 在此列表的指定位置上插入指定元素。
3、clear():从此列表移除所有元素。
4、contains(Object o) :如果此列表包含指定的元素,则返回 true。
5、equals(Object o) :比较指定对象与此列表的相等性。
6、get(int index) : 返回列表中指定位置的元素。
7、hashCode() : 返回此列表的哈希码值。
8、indexOf(E e, int index) : 返回第一次出现的指定元素在此列表中的索引,从 index 开始向前搜索,如果没有找到该元素,则返回 -1。
9、indexOf(Object o) :返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
10、isEmpty() :如果此列表不包含任何元素,则返回 true。
11、iterator() :返回以恰当顺序在此列表元素上进行迭代的迭代器,返回值为 Iterator。
12、lastIndexOf(E e, int index) :返回最后一次出现的指定元素在此列表中的索引,从 index 开始向后搜索,如果没有找到该元素,则返回 -1。
13、lastIndexOf(Object o) : 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
14、remove(int index) :移除此列表指定位置上的元素。
15、remove(Object o) :从此列表移除第一次出现的指定元素(如果存在),返回值为 boolean。
16、set(int index, E element) :用指定的元素替代此列表指定位置上的元素。
17、size() :返回此列表中的元素数。
18、subList(int fromIndex, int toIndex) :返回此列表中 fromIndex(包括)和 toIndex(不包括)之间部分的视图,返回值为 List 。

CopyOnWriteArrayList 源码

添加元素 add(E e)

加锁-> 拷贝一个新的数组 -> 添加新元素 -> 引用指向新的数组

  1. public boolean add(E e) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. Object[] elements = getArray();
  6. int len = elements.length;
  7. Object[] newElements = Arrays.copyOf(elements, len + 1);
  8. newElements[len] = e;
  9. setArray(newElements);
  10. return true;
  11. } finally {
  12. lock.unlock();
  13. }
  14. }

指定位置添加元素 add(int index, E element)

加锁-> 生成一个新的数组 -> 拷贝0到指定位置index到新数组 ->拷贝指定位置index到结束的所有元素->指定位置添加新元素 -> 引用指向新的数组

  1. public void add(int index, E element) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. Object[] elements = getArray();
  6. int len = elements.length;
  7. if (index > len || index < 0)
  8. throw new IndexOutOfBoundsException("Index: "+index+
  9. ", Size: "+len);
  10. Object[] newElements;
  11. int numMoved = len - index;
  12. if (numMoved == 0)
  13. newElements = Arrays.copyOf(elements, len + 1);
  14. else {
  15. // 分段拷贝
  16. newElements = new Object[len + 1];
  17. System.arraycopy(elements, 0, newElements, 0, index);
  18. System.arraycopy(elements, index, newElements, index + 1, numMoved);
  19. }
  20. newElements[index] = element;
  21. setArray(newElements);
  22. } finally {
  23. lock.unlock();
  24. }
  25. }

元素不存在才添加 addIfAbsent(E e)

  1. public boolean addIfAbsent(E e) {
  2. Object[] snapshot = getArray();
  3. return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false : addIfAbsent(e, snapshot);
  4. }
  5. private boolean addIfAbsent(E e, Object[] snapshot) {
  6. final ReentrantLock lock = this.lock;
  7. lock.lock();
  8. try {
  9. Object[] current = getArray();
  10. int len = current.length;
  11. if (snapshot != current) {
  12. // Optimize for lost race to another addXXX operation
  13. int common = Math.min(snapshot.length, len);
  14. for (int i = 0; i < common; i++)
  15. if (current[i] != snapshot[i] && eq(e, current[i]))
  16. return false;
  17. if (indexOf(e, current, common, len) >= 0)
  18. return false;
  19. }
  20. Object[] newElements = Arrays.copyOf(current, len + 1);
  21. newElements[len] = e;
  22. setArray(newElements);
  23. return true;
  24. } finally {
  25. lock.unlock();
  26. }
  27. }

CopyOnWriteArrayList数据不一致性

  1. import java.util.Iterator;
  2. import java.util.List;
  3. import java.util.concurrent.CopyOnWriteArrayList;
  4. public class Test {
  5. public static void main(String[] args) throws InterruptedException {
  6. List list = new CopyOnWriteArrayList();
  7. list.add("a");
  8. list.add("b");
  9. list.add("c");
  10. list.add("d");
  11. list.add("e");
  12. Thread thread = new Thread(() -> {
  13. list.set(0, "z");
  14. list.remove(2);
  15. list.remove(3);
  16. System.out.println(Thread.currentThread().getName() + ": " + list);
  17. }, "A");
  18. // 这里先获取了引用 然后启动线程修改
  19. Iterator iterator = list.iterator();
  20. /*
  21. // CopyOnWriteArrayList迭代器获取的就是数组的引用
  22. public Iterator<E> iterator() {
  23. return new COWIterator<E>(getArray(), 0);
  24. }
  25. */
  26. thread.start();
  27. thread.join();
  28. while (iterator.hasNext()){
  29. System.out.println(iterator.next());
  30. }
  31. }
  32. }

A: [z, b, d] a

b

c

d

e