一、集合类不安全问题 之 ArrayList。
1、ArrayList 不安全Demo展示:
package com;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ListNoSafeDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
1.1、运行结果:出现 并发修改异常:java.util.ConcurrentModificationException
1.2、出现异常的原因是:多个线程对同一个资源并发争抢修改导致。
1.3、第一种解决方法:
1.3.1、可以使用Vector类。
package com;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
public class ListNoSafeDemo {
public static void main(String[] args) {
List<String> list = new Vector<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
1.3.2、运行结果:没有出现 并发修改异常。
1.3.3、注意:虽然Vector类不报错,但是建议尽量不使用Vector,因为Vector类的add方法加了synchronized同步锁修饰,并发度低。而且Vector是JDK1.0版本的,而ArrayList是JDK1.2版本的,众所周知,高版本的肯定比低版本的要好点。
1.4、第二种解决办法:
1.4.1、使用 Collections.synchronizedList(new ArrayList()); 方法。
package com;
import java.util.*;
public class ListNoSafeDemo {
public static void main(String[] args) {
List<String> list = Collections.synchronizedList(new ArrayList());
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
1.4.2、运行结果:没有出现 并发修改异常。
1.5、第三种解决方法:
1.5.1、使用 new CopyOnWriteArrayList<>(); 写时复制的方法。
package com;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class ListNoSafeDemo {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
1.5.2、运行结果:没有出现 并发修改异常。
1.5.3、new CopyOnWriteArrayList<>();下add()方法底层原理讲解:
写时复制 copyOnWrite 容器即写时复制的容器
往容器添加元素的时候,不直接往当前容器object[]添加,而是先将当前容器object[]进行 copy 复制出一个新的object[]
newElements 然后向新容器object[] newElements 里面添加元素 添加元素后, 再将原容器的引用指向新的容器
setArray(newElements); 这样的好处是可以对copyOnWrite容器进行并发的读,而不需要加锁
因为当前容器不会添加任何容器.所以copyOnwrite容器也是一种 读写分离的思想,读和写不同的容器.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(); } }