写实复制原理(copy-on-write)

CopyOnWrite特点

读写并发时无需加锁,一般用于读多写少的操作,但要注意的是,读数据不能保证实时性

以CopyOnWriteArrayList源码进行分析

属性

// 显示操作的重入锁对象
final transient ReentrantLock lock = new ReentrantLock();
// 用来存储元素的对象数组
private transient volatile Object[] array;

构造器

// 默认构造器
public CopyOnWriteArrayList() {
    setArray(new Object[0]); // 数组长度为0
}

// 给定一个Collection容器来构建CopyOnWriteArrayList实例
public CopyOnWriteArrayList(Collection<? extends E> 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);
}

// 给定一个数组来构建CopyOnWriteArrayList实例
public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

写方法【写时复制】

add(E e) - 添加一个元素

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    // 上锁
    lock.lock();
    try {
        // 获取对象数组
        Object[] elements = getArray(); // =========== 旧对象数组
        int len = elements.length;
        // 复制原来的对象数组,并返回一个比源对象数组长度 +1 的对象数组 
        Object[] newElements = Arrays.copyOf(elements, len + 1); // =========== 新对象数组
        // 将元素添加到对象数组的末尾
        newElements[len] = e;
        // 替换内部对象数组的引用
        setArray(newElements);
        return true;
    } finally {
        // 解锁
        lock.unlock();
    }
}

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

public void add(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock(); 
    try {
        // 获取内部对象数组(旧对象数组)
        Object[] elements = getArray();// =========== 旧对象数组
        // 旧对象数组长度
        int len = elements.length; 
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);
        Object[] newElements;
        int numMoved = len - index; // 对象数组最后的下标
        if (numMoved == 0)
            // 返回一个全新的数组
            newElements = Arrays.copyOf(elements, len + 1); // =========== 新对象数组
        else {
            newElements = new Object[len + 1]; // =========== 新对象数组
            // 将elements下标为0开始的元素复制到newElements下标为[0,index)处
            System.arraycopy(elements, 0, newElements, 0, index);
            // 将elements下标为index开始的元素复制到newElements下标为[index+1,numMoved)处
            System.arraycopy(elements, index, newElements, index + 1,numMoved);
        }
        // 将element插入到newElements的index处
        newElements[index] = element;
        // 替换内部对象数组的引用
        setArray(newElements);
    } finally {
        lock.unlock();
    }
}

set(int index, E element) - 替换指定位置的元素

public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray(); // =========== 旧对象数组
        // index位置的旧元素
        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();
    }
}

读方法

final Object[] getArray() {
    return array; // 获取内部对象数组
}

// 根据下标获取元素
public E get(int index) {
    return get(getArray(), index);
}

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

使用测试

public class Main {

    public static void main(String[] args) {

        CopyOnWriteArrayList<String> cow = new CopyOnWriteArrayList<String>();
        cow.add("a1");
        cow.add("a2");
        cow.add(1, "a1.5");
        System.out.println("=====" + cow + "=====");
        
        cow.set(1, "hehe");
        System.out.println("=====" + cow + "=====");
        
        /**
         * output:
                =====[a1, a1.5, a2]=====
                =====[a1, hehe, a2]=====
         */
        
    }
}

猜你喜欢

转载自www.cnblogs.com/tandi19960505/p/9890336.html
今日推荐