文章目录
4.1ArrayList线程不安全演示及原因
4.1.1 案例代码
首先,我们创建一个ArrayList集合。然后,先创建30个线程,每个线程干的活就是向集合里面添加内容(我这里添加的是UUID前8位)。代码如下:
package safe;
import java.util.ArrayList;
import java.util.UUID;
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
}).start();
}
}
}
4.1.2 报错
运行后可以看到报错ConcurrentModificationException
,还有就是我们可能出现添加的值为null的情况。具体的原因看下面的原理。
ConcurrentModificationException
是因为集合会对并发进行控制,modCount
是修改记录数,expectedModCount
是期望修改记录数;
初始化的时候 expectedModCount=modCount
;当发现不相等的时候就会抛出异常。
4.1.3 解决
解决办法,就是使用线程安全的集合和方法。有下面三种方式,也给出了相应的代码!
- Vector
- Collections.synchronizedList()
- CopyOnWriteArrayList 使用的ReentrantLock可重入锁
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//ArrayList<String> list = new ArrayList<>();
//解决1: Vector (底层synchronized加在方法上,效率太低)
//Vector<String> list = new Vector<>();
//解决2: Collections.syschronizedList() (代码块级别的synchronized)
//ArrayList<String> l = new ArrayList<>();
//List<String> list = Collections.synchronizedList(l);
//解决3: CopyOnWriteArrayList (写时复制技术,复制一份,添加好新的后,让所有线程再来读新的,在这之前都原来旧的)
CopyOnWriteArrayList<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);
}).start();
}
}
}
4.1.4 原理
对于原理可以参看线面这篇博客。看完只有一定会明白为什么出现问题,为什么报错,可以喝面试官有的聊了。
面试官问 : ArrayList 不是线程安全的,为什么 ?(看完这篇,以后反问面试官)_arraylist线程不安全的原因_小目标青年的博客-CSDN博客
4.2 HashSet线程不安全演示及原因
4.2.1 案例代码
还是一样,首先,我们创建一个HashSet集合。然后,先创建30个线程,每个线程干的活就是向集合里面添加内容(我这里添加的是UUID前8位)。代码如下:
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//********演示HashSet************//
HashSet<String> set = new HashSet();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
}).start();
}
}
}
//解决:CopyOnWriteArraySet
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
4.2.2 报错
运行后可以看到报错ConcurrentModificationException
4.2.3 解决
- CopyOnWriteArraySet
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//********演示HashSet************//
//HashSet<String> set = new HashSet();
//解决:CopyOnWriteArraySet
CopyOnWriteArraySet<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);
}).start();
}
}
}
4.2.4 原理
HashSet底层源码没有加synchronized关键字做并发的控制,会导致安全问题。
CopyOnWriteArraySet底层还是CopyOnWriteArrayList。所以是线程安全的。下面是流程的分析图:
4.3 HashMap线程不安全演示及原因
4.3.1 代码
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//********演示HashMap************//
Map<String,String> map = new HashMap<>();
for (int i = 0; i < 30; i++) {
String key = String.valueOf(i);
new Thread(() -> {
map.put(key,UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
}).start();
}
}
}
4.3.2 报错
4.3.3 解决
- ConcurrentHashMap
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//Map<String,String> map = new HashMap<>();
//解决:ConcurrentHashMap
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
String key = String.valueOf(i);
new Thread(() -> {
map.put(key,UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
}).start();
}
}
}
4.3.4 原理
通过synchronized对代码块的修饰