java并发之CopyOnWriteArrayList

36 篇文章 0 订阅

原理

java普通的集合在遍历的时候不允许修改(add,remove,set)这样的操作,否则会抛出java.util.ConcurrentModificationException。为了支持集合的并发操作,于是CopyOnArrayList和CopyOnArraySet这样的数据结构就应运而生了.CopyOnArrayList这样的数据结构实现的思想是读写分离,通俗的讲在添加元素的时候,先将当前容器进行copy,复制出一个新容器,然后在新容器中添加元素.

在CopyOnArrayList中容器就是如下数组

private transient volatile Object[] array;

利用transient实现线程间的可见性

读取数据时候无需加锁,直接读取,这也是它比Vector更快的原因,get代码如下

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

该数据结构add代码如下

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    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();
    }
}

首先它需要获得操作修改集合结构的锁,然后利用getArray()获取全部的元素数组,然后利用copyOf复制全部的元素得到新组数,新数组比原来的数组size增1,增加的元素位置是新元素所在的位置,然后修改CopyOnArrayList中array的引用.

其它的比如remove方法原理也一样,不再赘述.CopyOnWriteArrayList更适合读多写少的并发场景,并且由于CopyOnWriteArrayList在读数据时不需要加锁所以它比Vector性能更好

问题

细心的读者会发现这个数据结构存在两个问题
1.内存占用
在A线程写操作的时候如果有B线程正在读,内存会同时存在两个对象,一个是A线程写完后新的array引用另一个是B线程正在读的旧引用,两份对象就会造成内存的浪费,如果内存占用过大会造成频繁的GC降低系统的性能
2.数据一致性
上面提到CopyOnArrayList可能会存在2份对象,那么数据一致性问题就来了,A线程刚写的数据,B线程可能会无法读到,如果我们对数据一致性要求很高,写完数据其它线程必须马上知道,请不要使用该数据结构

学以致用

在JDK中只实现了CopyOnArrayList和CopyOnArraySet,在理解了原理之后我们可以自己实现CopyOnArrayMap,该CopyOnArrayMap与ConcurrentHashMap相比在读取性能上更快

public class CopyOnArrayMap<K, V> {

    private volatile Map<K, V> map = new HashMap<>();

    private final Lock lock = new ReentrantLock();

    public void put(K key, V value) {
        lock.lock();
        try {
            map = new HashMap<>(map);
            map.put(key, value);
        } finally {
            lock.unlock();
        }
    }

    public void remove(K k) {
        lock.lock();
        try {
            map = new HashMap<>(map);
            map.remove(k);
        } finally {
            lock.unlock();
        }
    }

    public V get(K key) {
        return map.get(key);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值