并发下的集合
在上篇我们学到了并发包下的阻塞队列,接下来继续学习并发下的集合,并发下使用普通的集合是存在问题的,需要对其解决
1.1、传统解决方案
不说啥了,先来简简单单的测试一下:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
运行后抛出异常:Exception in thread "8" java.util.ConcurrentModificationException
(并发修改异常)
解决方案:
既然ArrayList
非线程安全,那么我们就是使用线程安全的类,比如:
-
Vector:使用
List
接口的实现类Vector
。-
List<String> list = new Vector<>();
-
-
工具类转换:使用
Collections
工具类的相关Synchronized
方法-
List<String> list = Collections.synchronizedList(new ArrayList<>());
-
以上两个实现方案中,对于数据处理都是直接采用锁进行解决,可查看源码,这样的情况会非常顺耗性能
1.2、JUC下的解决方案
1.2.1、CopyOnWriteArrayList
一、CopyOnwrite介绍
写时复制, 即在往集合中添加数据的时候,先拷贝一份存储的数组,然后添加元素到这份副本中,然后用副本去替换原先的数组。
源码:
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();
}
}
二、特点
- 相较于读写锁,写时复制在读取的时候可以写入的 ,这样省去了读写之间的资源竞争;
- 在Java类库的写时复制集合中:读取的时候不加锁,写的时候加锁
- 写的过程中,会额外复制一份数组,顺耗空间、复制过程也会顺耗时间
三、使用
使用非常简单,直接创建写时复制集合,调用API即可
package com.migu;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
public class Test {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
1.2.2、ConcurrentHashMap
一、ConcurrentHashMap介绍
并发下线程安全的HashMap
集合,Key
和值都不允许为null
,更深入的话我也不懂了
二、使用
使用也非常简单
-
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();