集合并发修改异常分析及解决

目录

 

一.集合类不安全之ArrayList

1.不安全代码演示

2.异常现象

3.导致原因

4.解决方式

二.集合类不安全之HashSet

1.示例代码

2.异常现象

3.解决方式

①:Collections.synchronizedSet();

②:new CopyOnWriteArraySet();   

三.集合类不安全之Map

1.代码演示

2.异常现象

3.解决方式

①:Collections.synchronizedMap(new HashMap<>());

②:new ConcurrentHashMap();   //TODO 分析源码


一.集合类不安全之ArrayList

1.不安全代码演示

 ArrayList<String> array = new ArrayList<String>();
        //开启10个线程
        for (int i=0;i<30;i++){
            new Thread(()->{
                array.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(array);     //注:sout的array时需要走list的迭代器,只有边遍历边修改才会出现并发修改异常(重要)
            },String.valueOf(i)).start();
        }

2.异常现象

简单描述为:30个人在花名册签到,由于arraylist的add方法没有synchronized,一个同学在名字还未写完时被另一个同学抢走,导致数据不一致,并发修改异常。

3.导致原因

在输出打印array时(读)时,需要遍历list从而输出集合中的每一个元素,这时出现并发修改异常

4.解决方式

① Vector实现类,其add方法是加过锁的(数据一致性一定能得到保证,但并发性急剧下降),其与arraylist相比即“安全与效率的取舍

/**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

② Collections.synchronizedList()

List<String> array = Collections.synchronizedList(new ArrayList<String>());
        //开启10个线程
        for (int i=0;i<30;i++){
            new Thread(()->{
                array.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(array);
            },String.valueOf(i)).start();
        }

③ juc包 CopyOnWriteArrayList类 

List<String> array = new CopyOnWriteArrayList<>();
        //开启10个线程
        for (int i=0;i<30;i++){
            new Thread(()->{
                array.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(array);     //sout的array需要走list的迭代器,只有边遍历边修改才会出现并发修改异常
            },String.valueOf(i)).start();
        }

其是一种读写分离的思想,共享读,写时将共享变量(花名册)拷贝,并扩容一个长度追加到末尾

源码分析:copyOnWriter容器即写时复制容器,往数组中添加元素时,不直接在elements数组中添加,而是先将elements 进行Copy复制出新容器newElements,这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite的读和写是不同的容器。所以适合读多写少的场景。

写:

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#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();
        }
    }

读:

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

    /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        return get(getArray(), index);
    }

二.集合类不安全之HashSet

1.示例代码

    HashSet<String> strings = new HashSet<>();
        //开启10个线程
        for (int i=0;i<30;i++){
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);     //sout的set需要走list的迭代器,只有边遍历边修改才会出现并发修改异常
            },String.valueOf(i)).start();
        }

2.异常现象

3.解决方式

①:Collections.synchronizedSet();

    Set<String> strings = Collections.synchronizedSet(new HashSet<String>());
        //开启10个线程
        for (int i=0;i<30;i++){
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);     //sout的set需要走list的迭代器,只有边遍历边修改才会出现并发修改异常
            },String.valueOf(i)).start();
        }

②:new CopyOnWriteArraySet();   

翻源码意外收获:

三.集合类不安全之Map

1.代码演示

HashMap<Object, Object> map = new HashMap<>();
        //开启10个线程
        for (int i=0;i<30;i++){
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);     //sout的map需要走map的迭代器,只有边遍历边修改才会出现并发修改异常
            },String.valueOf(i)).start();
        }

2.异常现象

3.解决方式

①:Collections.synchronizedMap(new HashMap<>());

②:new ConcurrentHashMap();   //TODO 分析源码

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值