1)List 不安全
//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
List<Object> arrayList = new ArrayList<>();
for(int i=1;i<=10;i++){
new Thread(()->{
arrayList.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(arrayList);
},String.valueOf(i)).start();
}
}
}
会导致 java.util.ConcurrentModificationException 并发修改异常!
ArrayList 在并发情况下是不安全的
解决方案:
public class ListTest {
public static void main(String[] args) {
/**
* 解决方案
* 1. List<String> list = new Vector<>();
* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3. List<String> list = new CopyOnWriteArrayList<>();
*/
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <=10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
- CopyOnWriteArrayList:写入时复制! COW 计算机程序设计领域的一种优化策略
核心思想是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者视图修改资源内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。读的时候不需要加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList。多个线程调用的时候,list,读取的时候,固定的,写入(存在覆盖操作);在写入的时候避免覆盖,造成数据错乱的问题;
CopyOnWriteArrayList比Vector厉害在哪里?
Vector底层是使用synchronized关键字来实现的:效率特别低下。
CopyOnWriteArrayList使用的是Lock锁,效率会更加高效!
2)set 不安全
Set和List同理可得: 多线程情况下,普通的Set集合是线程不安全的;
解决方案还是两种:
- 使用Collections工具类的synchronized包装的Set类
- 使用CopyOnWriteArraySet 写入复制的JUC解决方案
public class SetTest {
public static void main(String[] args) {
/**
* 1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2. Set<String> set = new CopyOnWriteArraySet<>();
*/
// Set<String> set = new HashSet<>();
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
HashSet底层是什么?
hashSet底层就是一个HashMap;
3)Map不安全
//map 是这样用的吗? 不是,工作中不使用这个
//默认等价什么? new HashMap<>(16,0.75);
Map<String, String> map = new HashMap<>();
//加载因子、初始化容量
- 默认加载因子是0.75,默认的初始容量是16
同样的HashMap基础类也存在并发修改异常!
public class MapTest {
public static void main(String[] args) {
//map 是这样用的吗? 不是,工作中不使用这个
//默认等价什么? new HashMap<>(16,0.75);
/**
* 解决方案
* 1. Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
* Map<String, String> map = new ConcurrentHashMap<>();
*/
Map<String, String> map = new ConcurrentHashMap<>();
//加载因子、初始化容量
for (int i = 1; i < 100; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
TODO:研究ConcurrentHashMap底层原理: