其他分享
首页 > 其他分享> > CopyOnWriteArrayList 是如何保证线程安全的?

CopyOnWriteArrayList 是如何保证线程安全的?

作者:互联网

1. 回顾 ArrayList

ArrayList 是基于数组实现的动态数据,是线程不安全的。例如,我们在遍历 ArrayList 的时候,如果其他线程并发修改数组(当然也不一定是被其他线程修改),在迭代器中就会触发 fail-fast 机制,抛出 ConcurrentModificationException 异常。

示例程序

List<String> list = new ArrayList();
list.add("xiao");
list.add("peng");
list.add(".");

Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    // 可能抛出 ConcurrentModificationException 异常
    iterator.next();
}

要实现线程安全有 3 种方式:

Collections.synchronizedList 包装类的原理很简单,就是使用 synchronized 加锁,源码摘要如下:

Collections.java

public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
            new SynchronizedRandomAccessList<>(list) :
            new SynchronizedList<>(list));
}

// 使用 synchronized 实现线程安全
static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
    final List<E> list;

    public boolean equals(Object o) {
        if (this == o) return true;
        synchronized (mutex) {return list.equals(o);}
    }
    public int hashCode() {
        synchronized (mutex) {return list.hashCode();}
    }

    public E get(int index) {
        synchronized (mutex) {return list.get(index);}
    }
    public E set(int index, E element) {
        synchronized (mutex) {return list.set(index, element);}
    }
    public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
    public E remove(int index) {
        synchronized (mutex) {return list.remove(index);}
    }
		...
}

如果我们将 ArrayList 替换为 CopyOnWriteArrayList,即使其他线程并发修改数组,也不会抛出 ConcurrentModificationException 异常,这是为什么呢?


2. CopyOnWriteArrayList 的特点

CopyOnWriteArrayList 和 ArrayList 都是基于数组的动态数组,封装了操作数组时的搬运和扩容等逻辑。除此之外,CopyOnWriteArrayList 还是用了基于加锁的 “读写分离” 和 “写时复制” 的方案解决线程安全问题:

所以,使用 CopyOnWriteArrayList 的场景一定要保证是 “读多写少” 且数据量不大的场景,而且在写入数据的时候,要做到批量操作。否则每个写入操作都会触发一次复制,想想就可怕。举 2 个例子:

标签:系统,塔建,浏览器,请求服务,Spring
来源: