首先,我们创建一个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();
}
}
}
运行后可以看到报错ConcurrentModificationException
,还有就是我们可能出现添加的值为null的情况。具体的原因看下面的原理。
ConcurrentModificationException
是因为集合会对并发进行控制,modCount
是修改记录数,expectedModCount
是期望修改记录数;
初始化的时候 expectedModCount=modCount
;当发现不相等的时候就会抛出异常。
解决办法,就是使用线程安全的集合和方法。有下面三种方式,也给出了相应的代码!
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//ArrayList list = new ArrayList<>();
//解决1: Vector (底层synchronized加在方法上,效率太低)
//Vector list = new Vector<>();
//解决2: Collections.syschronizedList() (代码块级别的synchronized)
//ArrayList l = new ArrayList<>();
//List 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();
}
}
}
对于原理可以参看线面这篇博客。看完只有一定会明白为什么出现问题,为什么报错,可以喝面试官有的聊了。
面试官问 : ArrayList 不是线程安全的,为什么 ?(看完这篇,以后反问面试官)_arraylist线程不安全的原因_小目标青年的博客-CSDN博客
还是一样,首先,我们创建一个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<>();
运行后可以看到报错ConcurrentModificationException
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//********演示HashSet************//
//HashSet 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();
}
}
}
HashSet底层源码没有加synchronized关键字做并发的控制,会导致安全问题。
CopyOnWriteArraySet底层还是CopyOnWriteArrayList。所以是线程安全的。下面是流程的分析图:
/**
* @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();
}
}
}
/**
* @author LWJ
* @date 2023/6/18
*/
public class ThreadUnSafeDemo {
public static void main(String[] args) {
//Map 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();
}
}
}