集合不安全
1. ArrayList线程不安全演示-什么故障?什么原因?怎么解决?
ArrayList底层是一个数组,默认大小10,超过就扩容,扩原值的一半10+5=15
线程不安全,因为add方法没有加锁。
不安全案例:
public class ContainerNotSafeDemo {
/**
* 1. 故障
* java.util.ConcurrentModificationException 并发修改异常
*
* 2. 原因
* 线程并发修改导致,线程1正在写入,线程2抢占资源,导致数据不一致。
*
* 3. 解决
* 1 new Vector(); add方法加锁,线程安全,并发性差。
* 2 Collections.synchronizedList(new ArrayList<>()); 包装成安全的,还是加锁,并发性差。
* 3 new CopyOnWriteArrayList<>(); juc的类,写时复制
* 4. 优化
*/
public static void main(String[] args) {
// List<String> list = new ArrayList<>();
// List<String> list = new Vector<>();
// List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,4));
System.out.println(Thread.currentThread().getName()+"\t"+list);
},String.valueOf(i)).start();
}
}
}
2. CopyOnWriteArrayList原理?它有什么好?
参考:https://blog.csdn.net/linsongbin1/article/details/54581787
add源码:
public boolean add(E e) {
// 1. 加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 2. 拷贝数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 3. 新增元素到新数组
newElements[len] = e;
// 4. 将array引用指向新数组
setArray(newElements);
return true;
} finally {
// 解锁
lock.unlock();
}
}
解释写时复制:
- 写操作时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。整个add操作都是在锁的保护下进行的。
- 读操作时,如果写完成且引用指向新数组,则读到的是最新数据;否则读到的是原始数组数据。可见读操作是不加锁的
3. CopyOnWriteArrayList 缺点&使用场合
- 消耗内存。写操作,拷贝数组,消耗内存,数组大的话可能导致gc
- 不能实时读。拷贝新增需要时间,读到的可能是旧数据,能保证最终一致性,但不满足实时要求。
因此,适合读多写少的场景。
CopyOnWriteArrayList透露的思想
- 读写分离,提高并发
- 不能满则实时性,可保证最终一致性
- 通过另辟空间,来解决并发冲突
4. 集合类不安全Set
Set同理
HashSet > Collections.synchronizedSet() > CopyOnWriteArraySet
且CopyOnWriteArraySet底层还是用的CopyOnWriteArrayList
HashSet底层是HashMap, add(key,一个常量)
5.集合类不安全Map
Map类似
HashMap > Collections.synchronizedMap() > ConcurrentHashMap