2019年互联网(1.4)
1.4.我们知道ArrayList是线程不安全,请编写一个不安全的案例并给出解决方案
问题描述
** 已知:list、Set、Map在多线程的环境下,会尝试异常,也就是发生线程不安全事件 * 1. 故障现象 * java.util.ConcurrentModificationException * 2. 导致原因 * 多线程并发争取同一个资源,且未加锁 *3.解决方法 * 3.1 new Vector<>(); * 3.2 Collections.synchronizedList(new ArrayList<String>()); * 3.3 new CopyOnWriteArrayList(); //写时复制 * 4.优化建议(同样的错误不犯第二次)
案例一:list线程不安全
下面我们来简单看一下代码:
public class NotSafeDemo03 {
public static void main (String[] args) {
listNotSafe();
}
private static void listNotSafe () {
//出现问题的方法
// List<String> list = new ArrayList <>();
// 解决方法 1 ,采用底层采用 synchronized实现的Vector
// List <String> list = new Vector <>();
// 解决方法 2 ,采用Collections.synchronizedList来实现,还可以解决Set,Map中线程不安全的问题
// List<String> list =Collections.synchronizedList(new ArrayList <>());
// 解决方法 3 , 采用JUC中新方法,CopyOnWriteArrayList(); 写实复制, 底层采用可从重入锁来完成的
List <String> list = new CopyOnWriteArrayList();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf("A")).start();
}
}
}
在代码块中,我们可以清晰看到,list出现问题的方法,以及各种解决方案,读者可以试运行一下。
体验完以后,为什么会发生线程不安全事件呢
/ * 分析问题源头, 扩容事件
new ArrayList() 实际上是new了一个大小为10的object数组
* 存25个元素,会自动扩容,第一次 扩到15,(扩充原来值的一半)
HashMap的原值大小为16,扩容一次扩充一倍
* 采用Array.copyOf进行辅助
* 第二次扩容 扩到 22 (15的一半7.5,取整)
*/
为什么可以用上述三种方案解决呢,源码解释:

噢,原来他们都是List的子接口,而且查看底层源码,发现Vector前面就已经扩容好了,不会发现扩充导致的异常,CopyOnWriteArrayList,采用的是一种写时复制的策略。synchronizedList,就像他的名字一样,采用synchronized加锁实现。
写时复制 * CopyOnWrite 容器即写时复制的容器。往一个容器中添加新的元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行copy * 复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements 添加新元素,添加完元素以后 * 再将原来容器的引用指向setArray(newElement);这样做的好处就是可以对CopyOnWrite容器进并发的读,而且不需要加锁, * 即 CopyOnWrite也是一种读写分离的思想,读和写不同的容器
Set集合类可以采用CopyOnwriteHashSet,以及Map集合类使用专门的ConcurrentHashMap工具类

浙公网安备 33010602011771号