【java并发】java CopyOnWriteArrayList学习

大家好,我是walker
一个从文科自学转行的程序员~
爱好编程,偶尔写写编程文章和生活
欢迎关注公众号【I am Walker】,回复“电子书”,就可以获得200多本编程相关电子书哈~
我的gitee:https://gitee.com/shen-chuhao/walker.git 里面很多技术案例!

参考:CopyOnWriteArrayList真的完全线程安全吗

CopyOnWriteArrayList 是什么?

  • CopyOnWriteArrayList 是一个并发容器。有很多人称它是线程安全的,我认为这句话不严谨,缺少一个前提条件,那就是非复合场景下操作它是线程安全的。
  • CopyOnWriteArrayList(免锁容器)的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出 ConcurrentModificationException
  • 在CopyOnWriteArrayList 中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。

CopyOnWriteArrayList不安全案例

数组越界

如果这时候有第三个线程进行删除元素操作,读线程去读取容器中最后一个元素,读之前的时候容器大小为i,当去读的时候删除线程突然删除了一个元素,这个时候容器大小变为了i-1,读线程仍然去读取第i个元素,这时候就会发生数组越界。

package concurrentContainer.copyOnWrite;

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteTest {


    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        /**
         * 主线程往copyOnWrite数组中加入数据
         */
        for(int i = 0; i<5000; i++){
            list.add("a" + i);
        }



        /**
         * 该线程一直获取数组的最后一个元素
         */
        new Thread(()->{
            while (true) {
                if (list.size() > 0) {
                    String content = list.get(list.size() - 1);
                }else {
                    break;
                }
        }}).start();


        /**
         * 该数组一直移除元素
         */
        new Thread(()->{
            while (true) {
                if(list.size() <= 0){
                    break;
                }

                list.remove(0);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

输出结果: 抛出异常

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 4993
	at java.util.concurrent.CopyOnWriteArrayList.get(CopyOnWriteArrayList.java:388)
	at java.util.concurrent.CopyOnWriteArrayList.get(CopyOnWriteArrayList.java:397)
	at concurrentContainer.copyOnWrite.CopyOnWriteTest.lambda$main$0(CopyOnWriteTest.java:26)
	at java.lang.Thread.run(Thread.java:748)

因此可以知道,CopyOnWriteArrayList 的使用场景是读多写少的场景

有哪些优缺点?

优点

就是合适读多写少的场景。

缺点

消耗内存 不能满足实时性 代价大

  1. 由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致 young gc 或者 full gc。
  2. 不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个 set 操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求。
  3. 由于实际使用中可能没法保证 CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次 add/set 都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。

CopyOnWriteArrayList 的设计思想 3`

  1. 读写分离,读和写分开
  2. 最终一致性
  3. 使用另外开辟空间的思路,来解决并发冲突

源码解析

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

setArray

final void setArray(Object[] a) {
        array = a;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WalkerShen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值