Java并发------CopyOnWriteArrayList

前言

我们所知 Map 双列集合的 Hashtable 和 Collection 单列集合的 Vector 是线程安全的,但是这两种方式都是简单粗暴的使用 synchronized 对涉及方法进行加锁,效率很低下。所以 Java 并发包中对 HashMap 提出了一种效率更高的一种线程安全的方式,也就是 ConcurrentHashMap,并且在 Jdk8 中性能得到了再次提升。

而对于 List 和 Set,Java 并发包也提出了一种解决方式,就是 CopyOnWriteArrayList 和 CopyOnWriteArraySet。它们采取一种读写分离的并发策略,我们也可以称这种容器为“写时复制器”,即读数据是一种无锁操作,写数据会将当前容器复制一份,添加或删除时都是对新容器中的元素进行操作,读数据依旧是老容器,只有在添加或删除完成时,对象引用指向新生成的容器。

这种方式的优缺点显而易见,对于读多写少的场景非常适用,也不会出现并发修改异常 ConcurrentModificationException,缺点就是占用内存,频繁 GC,不能保证读和写的强一致性。

源码

下面是 Jdk8 的 CopyOnWriteArrayList 源码,很容易看懂:

1)get 方法是在无锁的状态进行:

public E get(int index) {
    return get(getArray(), index);
}

2)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();
    }
}

3)remove 同样:

public E remove(int index) {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lock();
    try {
        // 原容器
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = get(elements, index);
        int numMoved = len - index - 1;
        // 删除元素的索引位置正好时数组的最后一位,则直接 copy 此索引位置之前的元素即可
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        // 删除元素的索引位置是其他位置,分成两部分 copy
        else {
            Object[] newElements = new Object[len - 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index,
                    numMoved);
            setArray(newElements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

其实 Java 并发包对于 List 和 Set 提出的这种解决方式,没有太多的从技术上解决并发问题,我们在使用时也要针对不同场景选择合理的一种解决方式即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值