0
点赞
收藏
分享

微信扫一扫

集合类不安全的问题

奋斗De奶爸 2021-09-30 阅读 65
高并发

演示

先上代码

/**
 * 集合类不安全的问题
 */
public class ContainerNotSafeTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

各位多执行几次,会发现输出并不一致
输出1:

[76d1ea7c, 35311fea, f6c74b7d]
[76d1ea7c, 35311fea, f6c74b7d]
[76d1ea7c, 35311fea, f6c74b7d]

输出2:

[null, c1720792, 8daa63e6]
[null, c1720792, 8daa63e6]
[null, c1720792, 8daa63e6]

输出3:

[6330da8b, 8a608765]
[6330da8b, 8a608765]
[6330da8b, 8a608765, a9c79c0d]

输出4:如果出不来效果的可能是电脑太好了,把线程改为30

[b1ed10d8, 5cfb76e5, 55bda327]
Exception in thread "2" Exception in thread "0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
    at java.util.ArrayList$Itr.next(ArrayList.java:859)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at collections.ContainerNotSafeTest.lambda$main$0(ContainerNotSafeTest.java:22)
    at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
    at java.util.ArrayList$Itr.next(ArrayList.java:859)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at collections.ContainerNotSafeTest.lambda$main$0(ContainerNotSafeTest.java:22)
    at java.lang.Thread.run(Thread.java:748)

故障现象

1、元素为null
2、java.util.ConcurrentModificationException

为什么会导致这些问题呢?

1、元素为null
因为ArrayList初始是会定义数组,并不会创建,所以高并发情况下,可能多个线程同时创建了底层数组,并放入元素,但是只有一个数组被使用,导致其他创建数组的线程将值付给了自己创建的数组

2、并发修改异常
并发争抢修改数据

解决方案

1、自行加锁(不建议,有现成的,何必自己加呢?)
2、使用Vector(不建议,并发性下降)
3、Collections.synchronizedList(new ArrayList())
4、CopyOnWriteArrayList<E> 写时复制

什么是写时复制

夫指出一个新的容器Obejct[] newElements,然后向新的容器里添加元素,添加完成后,再将原来的容器引用指向新容器setArray(new Elements),这样做的好处是可以对CopyOnWrite容器进行并发的度,而不需要加锁,因为当前容器并不会添加任何元素,所以CopyOnWrite容器是一种读写分离的思想,读和写不同的容器

优化建议

扩展

HashSet :
Collections.synchronizedSet(new HashSet())
CopyOnWriteArraySet<>() ——底层还是CopyOnWriteArrayList

HashMap:
Collections.synchronizedMap(new HashMap())
ConcurrentHashMap


内容均来源于学习资料,在学习过程中进行记录,如有侵权联系作者进行删除

Change the world by program

举报

相关推荐

0 条评论