并发专题之---并发中集合出现的问题

ArrayList

问题的出现

案例1

public class SingletonDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int j = 0; j <30 ; j++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8).toString());
                list.forEach(o -> System.out.println(o));
            }).start();
        }
    }
}

出现问题:
java.util.ConcurrentModificationException

在《java并发程序设计》一书也提到一个例子
在这里插入图片描述
案例2

public class SingletonDemo {
    static List arrayList  = new ArrayList();
    public static void main(String[] args) {
        new Thread(()->{
            for (int i = 0; i<1000 ; i++) {
                   arrayList.add(i);
            }
        },"t1").start();

        new Thread(()->{
            for (int i = 0; i<1000 ; i++) {
                arrayList.add(i);
            }
        },"t2").start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(arrayList.size());
    }

}
有三个情况  size = 2000
          java.lang.ArrayIndexOutOfBoundsException
          size<2000
          本书中给的建议,是使用Vector但是并不好,我下面我会给更好建议.

为什么会出现这个问题

假如我们举个例子,每来一个人进行签到,加入我们加锁,有人维持秩序,此时不允许代签,但是如果没有加锁,此时张三刚写上自己的名字的姓,还没有写完,另一个线程直接推开张三,打断了这个写的操作,此时就是并发修改异常.
刚开始的时候,所有的线程的引用对象为空的签名单,此时A签名签完后,告诉其他线程,你们别用过去没有我名字的签名单,你们应该引用有我名字的签名单,然后才能继续签名.

解决方案

1:使用Vector,但是性能会急剧下降
2:使用Collections工具类,同理后面相关的集合如map 也可以使用相同的办法,后面不在陈述.

public class SingletonDemo {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        List<String> list = Collections.synchronizedList(strings);
        for (int j = 0; j <30 ; j++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8).toString());
                list.forEach(o -> System.out.println(o));
            }).start();
        }
    }
}

3:使用JUC的并发类 CopyOnWriteArrayList (核心)

    List<String> list = new CopyOnWriteArrayList<>();
    for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

为什么CopyOnWriteArrayList可以解决

1:首先定义的数组,加上了volatile (如果对volatile的作用和底层不熟悉的同学,可以看看我写博客https://william.blog.csdn.net/article/details/102870414)
在这里插入图片描述
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();
        }
    }

HashSet

问题的出现

public class SingletonDemo {
    public static void main(String[] args) {
      Set list = new HashSet<>();
        for (int j = 0; j <30 ; j++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8).toString());
                list.forEach(o -> System.out.println(o));
            }).start();
        }
    }
}
出现的问题:java.util.ConcurrentModificationException

HashSet结构

面试的时候会有这样的场景
面试官: HashSet的底层是什么实现
我: HashMap
面试官:HashMap是key和value键值对的,为什么HashSet只添加一个值?

源码解决
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到hashset只在乎他的key,他的value为一个常量对象.

解决办法

这里直接将核心的,其他的解决思路和arraylist基本思想一样

使用CopyOnWriteArraySet

public class SingletonDemo {
    public static void main(String[] args) {
      Set list = new CopyOnWriteArraySet();
        for (int j = 0; j <30 ; j++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8).toString());
                list.forEach(o -> System.out.println(o));
            }).start();
        }
    }
}

为什么CopyOnWriteArraySet可以解决

1:可以看到CopyOnWriteArraySet底层实际上使用还是CopyOnWriteArrayList
在这里插入图片描述

Map

问题的出现

public class SingletonDemo {
    public static void main(String[] args) {
        Map map = new HashMap();
        for (int j = 0; j < 30; j++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8).toString());
                System.out.println(map);
            }).start();
        }
    }
}
出现的问题:ConcurrentModificationException

解决办法

你是不是还以为有一个CopyOnWriteHashMap? 但是并没有,他为我们提供了ConcurrentHashMap

public class SingletonDemo {
    public static void main(String[] args) {
        Map map = new ConcurrentHashMap();
        for (int j = 0; j < 30; j++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8).toString());
                System.out.println(map);
            }).start();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值