同时有30个线程去同时读写list集合
public static void listNotSafe(){
//采用Collections工具类将ArrayList变为线程安全的
List<String> list =new ArrayList<>();// Collections.synchronizedList(new ArrayList<>());//new Vector<>();//new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);//每次运行得到的结果都不相同
},String.valueOf(i)).start();
//如果循环次数增加到30次,会报java.util.ConcurrentModificationException异常
//Vector线程安全
}
}
由于线程不安全,则会报并发修改异常
> java.util.ConcurrentModificationException at
> java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at
> java.util.ArrayList$Itr.next(ArrayList.java:851) 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
> com.atguigu.juc.NotSafeDemo.lambda$listNotSafe$1(NotSafeDemo.java:68)
> at java.lang.Thread.run(Thread.java:748)
1.故障现象
- java.util.ConcurrentModificationException
- 2.导致原因
- 多个线程同时读写list,造成异常
- 3.解决方案
3.1 Vector
3.2 Collections.synchronizedList(new ArrayList<>();
3.3CopyOnWriteArrayList<>();
public static void listNotSafe(){
//采用Collections工具类将ArrayList变为线程安全的
List<String> list =new CopyOnWriteArrayList<>();// Collections.synchronizedList(new ArrayList<>());//new Vector<>();//new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);//每次运行得到的结果都不相同
},String.valueOf(i)).start();
//如果循环次数增加到30次,会报java.util.ConcurrentModificationException异常
//Vector线程安全
}
}
-
CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,
- 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。
- 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。
- 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
- 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
CopyOnWriteArrayList<>()的源码
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();
}
}
先创建一个lock锁,然后对底层的数组进行get并得到数组长度,然后复制一份长度加一的新数组,并把此事拿到锁的类的元素加入新数组,最后进行set并返回true,最后解锁。这就是CopyOnWriteArrayList的底层线程安全执行步骤
集合同理
public static void setNotSafe(){
Set<String> set = new CopyOnWriteArraySet<>();//Collections.synchronizedSet();//new HashSet<>();
new HashSet<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,8));
},String.valueOf(i)).start();
}
}
HashMap稍有变化,new的类为ConcurrentHashMap<>();
public static void mapNotSafe(){
Map<String,String> map =new ConcurrentHashMap<>();//Collections.synchronizedMap();// new HashMap<>();//数组+链表+红黑树D
//Map<String,String> map2= new HashMap<>(1000);//数组+链表+红黑树D
for (int i = 0; i < 30; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
}