ArrayList线程不安全,请编写一个不安全的案例,并且给出解决方案

不安全的案例

额,这里我踩坑了,在Junit中启动编写启动,一直启动不起来,后面百度才知道Junit中不能启动多线程(详细的可以去见这个https://blog.csdn.net/qq_40889460/article/details/101192003)

		System.out.println("----------线程不安全案例-----------");
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 3000; i++) {

            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,10));
                System.out.println(list);

            },String.valueOf(i)).start();

        }

出现了错误,并发修改错误
java.util.ConcurrentModificationException

解决方案

一、使用Vector (不推荐,性能太差)本质就是通过加上锁

 		System.out.println("----------解决方案-----------");
        //一、vector
        List<String> list = new Vector<>();
        for (int i = 0; i < 3000; i++) {

            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,10));
                System.out.println(list);

            },String.valueOf(i)).start();

        }

查看vector源码 方法加上synchronized 性能很差

    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

二、第二种,使用工具类Collections包装ArrayList

//2、使用Collections工具类包装Arraylist
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 3000; i++) {

            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,10));
                System.out.println(list);

            },String.valueOf(i)).start();

        }

查看了Collections.synchronizedList()方法,将ArrayList包装,生成一个SynchronizedList对象

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

查看SynchronizedList里面的add方法

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

本质的解决方案 还是依赖于synchronized 在代码块中加锁,相对于Vector在方法上加锁,锁的范围小,性能会高一点

三、采用读写分离思想

        //3、采用读写分离思想 (性能最佳),写时复制
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 3000; i++) {

            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,10));
                System.out.println(list);

            },String.valueOf(i)).start();

        }

读写分离 :就是修改list的时候,是拷贝一份进行修改,其他的读 还是读原来的那一份,等写完,将原来那份失效

查看了CopyOnWriteArrayList 的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();
        }
    }

读的时候 是不用加锁的

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

对于其他的Map,Set 并发修改问题,都是采用一样的方案去解决

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值