JDK源码 -- CopyOnWriteArrayList

一、概念

类定义:

public class CopyOnWriteArrayList
    implements List, RandomAccess, Cloneable, java.io.Serializable 
  • 实现了List接口,拥有一组List通用的操作。
  • 实现了RandomAccess接口,可进行随机访问。
  • 实现了Cloneable接口,可进行浅层次拷贝。
  • 实现了Serializable接口,可进行序列化。

特点:

  • 读取操作完全不用加锁。
  • 写入操作不会阻塞读取操作。
  • 只有写入操作与写入操作需要进行同步。

原理:
CopyOnWriteArrayList类的写操作是通过创建底层数组的新副本来实现的。当List需要被修改的时候,并不直接修改原有数组对象,而是对原有数据进行一次拷贝,将修改的内容写入副本中。写完之后,再将修改完的副本替换成原来的数据,这样就可以保证写操作不会影响读操作了。

在多线程环境中,如果线程A执行写操作(如add、set等)之前线程B执行了读操作(如get、iterator等),那么在线程A执行写操作完成并更新了底层数组后,线程B执行读操作使用的底层数组仍然是旧的那一份,这就是弱一致性。

二、使用

//TestCopyOnWriteArrayList
public class TestCopyOnWriteArrayList {
    private static final String TAG = "CopyOnWriteArrayList";
    private CopyOnWriteArrayList list = new CopyOnWriteArrayList();

    public void testAdd() {
        list.add(null);
        list.add("AAA");
        list.add("BBB");
        list.addIfAbsent("CCC");
        list.addIfAbsent("CCC");
        Log.d(TAG, "zwm, add list: " + list);
    }

    public void testSet() {
        list.set(1, "AAAAA");
        Log.d(TAG, "zwm, set list: " + list);
    }

    public void testGet() {
        Log.d(TAG, "zwm, get index 1: " + list.get(1));
    }

    public void testRemove() {
        list.remove(null);
        Log.d(TAG, "zwm, remove list: " + list);
    }

    public void testSize() {
        Log.d(TAG, "zwm, size: " + list.size());
    }

    public void testListIterator() {
        ListIterator listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            Log.d(TAG, "zwm, listIterator item: " + listIterator.next());
        }
    }
}

//测试代码
private void testMethod() {
    Log.d(TAG, "zwm, testMethod");
    TestCopyOnWriteArrayList testCopyOnWriteArrayList = new TestCopyOnWriteArrayList();
    testCopyOnWriteArrayList.testAdd();
    testCopyOnWriteArrayList.testSet();
    testCopyOnWriteArrayList.testGet();
    testCopyOnWriteArrayList.testRemove();
    testCopyOnWriteArrayList.testSize();
    testCopyOnWriteArrayList.testListIterator();
}

//输出log
2019-08-17 11:05:55.960 zwm, testMethod
2019-08-17 11:05:55.962 zwm, add list: [null, AAA, BBB, CCC]
2019-08-17 11:05:55.962 zwm, set list: [null, AAAAA, BBB, CCC]
2019-08-17 11:05:55.962 zwm, get index 1: AAAAA
2019-08-17 11:05:55.962 zwm, remove list: [AAAAA, BBB, CCC]
2019-08-17 11:05:55.962 zwm, size: 3
2019-08-17 11:05:55.963 zwm, listIterator item: AAAAA
2019-08-17 11:05:55.963 zwm, listIterator item: BBB
2019-08-17 11:05:55.963 zwm, listIterator item: CCC

三、原理

重要参数

//锁对象
final transient ReentrantLock lock = new ReentrantLock();

//存放数据的数组
private transient volatile Object[] array;

构造函数

//创建一个元素个数为0的CopyOnWriteArrayList
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

//创建一个具有指定的Collection的元素的CopyOnWriteArrayList
public CopyOnWriteArrayList(Collection c) {
    Object[] elements;
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList)c).getArray();
    else {
        elements = c.toArray();
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    setArray(elements);
}

//创建一个具有指定的toCopyIn数组的元素的CopyOnWriteArrayList
public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

public boolean add(E e)

//插入一个元素
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //使用ReentrantLock加锁
    try {
        Object[] elements = getArray(); //获取底层数组
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1); //进行拷贝
        newElements[len] = e; //插入元素
        setArray(newElements); //更新底层数组,使用新数组替换旧数组
        return true;
    } finally {
        lock.unlock(); //在finally块中释放锁
    }
}

public E set(int index, E element)

//修改索引位置的元素
public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //使用ReentrantLock加锁
    try {
        Object[] elements = getArray(); //获取底层数组
        E oldValue = get(elements, index);

        if (oldValue != element) {
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len); //进行拷贝
            newElements[index] = element; //修改元素
            setArray(newElements); //更新底层数组,使用新数组替换旧数组
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock(); //在finally块中释放锁
    }
}

public E get(int index)

//获取索引位置的元素,不需要加锁
public E get(int index) {
    return get(getArray(), index);
}

private E get(Object[] a, int index) {
    return (E) a[index];
}

public boolean remove(Object o)

//删除与对象o相等的元素
public boolean remove(Object o) {
    Object[] snapshot = getArray();
    int index = indexOf(o, snapshot, 0, snapshot.length);
    return (index < 0) ? false : remove(o, snapshot, index);
}

private boolean remove(Object o, Object[] snapshot, int index) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //使用ReentrantLock加锁
    try {
        Object[] current = getArray(); //获取底层数组
        int len = current.length;
        if (snapshot != current) findIndex: {
            int prefix = Math.min(index, len);
            for (int i = 0; i < prefix; i++) {
                if (current[i] != snapshot[i] && eq(o, current[i])) {
                    index = i;
                    break findIndex;
                }
            }
            if (index >= len)
                return false;
            if (current[index] == o)
                break findIndex;
            index = indexOf(o, current, index, len);
            if (index < 0)
                return false;
        }
        Object[] newElements = new Object[len - 1]; //创建新数组
        System.arraycopy(current, 0, newElements, 0, index); //执行拷贝操作
        System.arraycopy(current, index + 1,
                         newElements, index,
                         len - index - 1);
        setArray(newElements); //更新底层数组,使用新数组替换旧数组
        return true;
    } finally {
        lock.unlock(); //在finally块中释放锁
    }
}

public int size()

//获取元素个数
public int size() {
    return getArray().length;
}

四、主题

ArrayList

你可能感兴趣的:(JDK源码 -- CopyOnWriteArrayList)