并发修改异常问题及解决
package cn.chen.demo.containernosafedemo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @ClassName: ListDemo
* @Description: 集合类不安全问题:List
* @author: chenlf
*
*/
public class ListDemo {
public static void main(String[] args) {
/*List<String> list = Arrays.asList("a","b","c");
list.forEach(System.out::println);*/
/*//ArrayList 不加锁的。并发性上升,但是牺牲了数据的一致性。
List<String> list = new ArrayList<>();*/
/*//1)Vector 是加了锁的。数据一致性可以保证,但是并发性下降了。
List<String> list = new Vector<>();*/
/*//2) Collections.synchronizedList(new ArrayList<>());
List<String> list = Collections.synchronizedList(new ArrayList<>());
*/
//3) java.util.concurrent.CopyOnWriteArrayList.CopyOnWriteArrayList<>()
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();
}
}
//报错:java.util.ConcurrentModificationException
/**
* 1.故障现象
* java.util.ConcurrentModificationException
*
* 2.导致原因
* 并发争抢修改导致。
* eg:1)买奶茶按号排队领取。正在领奶茶时,另外一个人跑过来拿,导致拿错了。
* 2)花名册签名,一个同学正在签名,另外一个同学过来抢夺,导致数据不一致异常。
*
* 3.解决方案
* 1) new Vector<>();
* 2) Collections.synchronizedList(new ArrayList<>());
* 3) new CopyOnWriteArrayList<>();
*
* 4.优化建议(不犯相同的错误)
*
*/
}
/**
输出结果:
java.util.ConcurrentModificationException
*/
写时复制
/** CopyOnWrite容器:写时复制容器
* 往一个容器添加元素时,不直接往当前容器Object[] elements添加,
* 而是对当前容器Object[]进行了复制。
* 复制出一个比当前容器长度+1的新容器Object[] newElements,
* 然后再往新容器里添加元素。
* 添加完元素之后,将原容器的引用指向新的容器setArray(newElements)
* 好处:可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
* 所以CopyOnWrite容器也是一种读写分离的思想。【读和写是不同的容器】
*/
//boolean java.util.concurrent.CopyOnWriteArrayList.add(E e)
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//获取当前数组
Object[] elements = getArray();
//当前数组长度
int len = elements.length;
//copy当前数组,并在当前数组的基础上长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//将新元素设置在新数组数组的最后,即索引为len
newElements[len] = e;
//将新数组再重新set回去
setArray(newElements);
return true;
} finally {
//释放锁
lock.unlock();
}
}
set
package cn.chen.demo.containernosafedemo;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
*
* @ClassName: SetDemo
* @Description: 集合类不安全问题Set
* @author: chenlf
*
*/
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>();
/**
* 底层是一个CopyOnWriteArrayList
* public CopyOnWriteArraySet() {
* al = new CopyOnWriteArrayList<E>();
* }
*/
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<>();//底层是hashMap
/**
hashMap是键值对,key-value,需要两个。
但是hashSet.add(E e) ,只需要一个。
原因看源码:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
由此可看出:e对应的就是map的key
另外:PRESENT 则是一个Object类型的常量(final修饰的)
*/
}
}
Map
package cn.chen.demo.containernosafedemo;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
*
* @ClassName: MapDemo
* @Description: 集合类安全问题Map
* @author: chenlf
*
*/
public class MapDemo {
public static void main(String[] args) {
/*//1) new ConcurrentHashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();*/
//2) Collections.synchronizedMap(new HashMap<>());
Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
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();
}
}
}