由ArrayList引发的一系列问题
写在前面:务必牢牢记忆java.util.ConcurrentModificationException、CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap
- new ArrayList()底层new了什么?数组
- 底层什么类型?Object 想想list.add 能装什么 啥都能装
- 以Java8为例初始值是多少?开始为空 add后10
- 存25个元素可以吗? 可以 ,底层扩容,怎么扩的,10》15,扩原值的一半
- 有没有看过源码,怎么搬家的?Arrays.copyOf()
- 第二次扩容扩到多少?22 hashmap扩容是原值的一倍16》32
- 7.ArrayList线程安全还是不安全?不安全
- 举例线程不安全的代码?
- Vector是线程安全的,为什么还要有ArrayList呢? 并发性和一致性本身就是相左的,以前没太大并发,锁表,只能一个线程操作
- 线程不安全怎么解决?
- 为什么写时复制安全?
package com.magic.juc0117;
/**
* @Description
* @Author Chelsea
* @Date 2020/1/17 17:18
*/
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
/**
* 1.故障现象
* java.util.ConcurrentModificationException
* 2.导致原因
* 多线程并发争抢同一个资源类并且没有加锁
* 3.解决方法
* ① List<String> list = new Vector<>();//源码加锁synchronized(重锁)
* 根据效率和安全性选择,电商ArrayList不多
* 不许用vector,请用②解决
* ②List<String> list = Collections.synchronizedList(new ArrayList<>());
* 如果②也不让用
* ③List<String> list = new CopyOnWriteArrayList();//写时复制
*
* 4.优化建议(同样的错误不能犯2次)
*/
/**
* 一个常问的题目:Collection 和 Collections有什么区别?
* Collection 接口 Collections集合接口的工具类
*/
public class NotSafeDemo03 {
public static void main(String[] args) {
listNotSafe();
//同理HashSet、HashMap也是线程不安全的,JUC也提供了对应的安全的类
// Set<String> set = new HashSet<>();
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
//HashMap ConcurrentHashMap
}
public static void listNotSafe(){
// List<String> list = new ArrayList();
// list.add("a");
// list.add("a");
// list.add("a");
// list.forEach(System.out::println); 这样不会不安全只有一个线程,下面for循环会异常ConcurrentModificationException
// 换成下面三种方式时异常解决
// List<String> list = new Vector<>();//源码加锁synchronized
// List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList();//写时复制
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();
}
}
/**
* 1.HashSet底层数据结构 HashMap 也不安全
* 选择CopyOnWriteArraySet
*/
/**
* HashMap
* ConcurrentHashMap
*/
}
回答最开始的问题,为什么写时复制安全?
下面是CopyOnWriteArrayList类add()方法的源码,由源码可以知道会加锁,而且可重入锁并发效率大大提高。
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();
}
}
学习整理于JUC.