演示
先上代码
/**
* 集合类不安全的问题
*/
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
内容均来源于学习资料,在学习过程中进行记录,如有侵权联系作者进行删除