一、并发修改异常
1.1 ArrayList代码示例
public class ContainerNotSafeDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i=1; i <=300; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
//java.util.concurrentModificationException
}
}
1.2 ConcurrentModificationException
多次运行,可能会出现ConcurrentModificationException并发修改异常
ArrayList的add方法并没有加锁,单线程情况下不会有什么问题,但是,多线程并发情况下,可能出现并发问题。
通俗解释
上课签到表,正常加锁情况下,老师(锁)在前面盯着,来一个人签到一个人,等前一个人写完名字后,后一个人才能写。假如,现在没有老师(锁),当小明正在写名字时,刚写了一半,嗖一下,被小刚抢走了,这就是并发修改异常。
1.3 解决方案之Vector
public class ContainerNotSafeDemo {
public static void main(String[] args) {
List<String> list = new Vector<>(); //new ArrayList<>();
for (int i=1; i <=300; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
//java.util.concurrentModificationException
/**
* 1 故障现象
* java.util.ConcurrentModificationException
* 2 导致原因
*
* 3 解决方案
* 3.1 new Vector<>();
* 3.2 Collections.synchronizedList(new ArrayList<>());
*
* 4 优化建议(同样的错误不犯第2次)
*/
}
}
Vector可以解决上面的问题,因为Vector的add方法加了synchronized锁,一致性得到了解决,但是效率急剧降低,所以,很少使用
1.4 解决方案之Collections.synchronizedList
public class ContainerNotSafeDemo {
public static void main(String[] args) {
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i=1; i <=300; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
//java.util.concurrentModificationException
/**
* 1 故障现象
* java.util.ConcurrentModificationException
* 2 导致原因
*
* 3 解决方案
* 3.1 new Vector<>();
* 3.2 Collections.synchronizedList(new ArrayList<>());
*
* 4 优化建议(同样的错误不犯第2次)
*/
}
}
此时,也可以解决,此外,还有其他的Collections类
二、写时复制
2.1 解决方案之写时复制(copyOnWrite)
public class ContainerNotSafeDemo {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i=1; i <=300; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
//java.util.concurrentModificationException
/**
* 1 故障现象
* java.util.ConcurrentModificationException
* 2 导致原因
* 并发争抢修改导致,参考我们的花名册签名情况
* 一个人正在写入,另外一个同学过来抢夺,导致数据不一致异常。并发修改异常
* 3 解决方案
* 3.1 new Vector<>();
* 3.2 Collections.synchronizedList(new ArrayList<>());
* 3.3 new CopyOnWriteArrayList();
*
* 4 优化建议(同样的错误不犯第2次)
*/
}
}
总之,没写完的时候,还没有返回true,我们用旧的;写完了返回了true,我们用新的
写时复制
CopyOnWrite容器,即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后往新的容器Object[] newElements里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
三、集合不安全类之Set
private static void setNotSafe() {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(set);
}, String.valueOf(i)).start();
}
new HashSet<>().add("a");
}
四、集合不安全类之Map
4.1 代码示例
public static void main(String[] args) {
//Map<String, String> map = new HashMap<>();
Map<String, String> map = new ConcurrentHashMap<>(); //Collections.synchronizedMap();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
4.2 总结
解决并发情况下的线程安全问题
List线程:CopyOnWriteArrayList
Set线程:CopyOnWriteHashSet
Map线程:ConcurrentHashMap
集合的全部讲解代码
public class ContainerNotSafeDemo {
public static void main(String[] args) {
Map<String,Object> map =new ConcurrentHashMap<>();
for (int i = 1; i <=30 ; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
private static void setNotSafe() {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <=30 ; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
private static void listNotSafe() {
List<String> list = new CopyOnWriteArrayList<>(); //写时复制技术
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}