我们知道ArrayList是线程不安全的,请编码写一个不安全的案例并给出解决方案?
一、ArrayList案例
1. 故障现象
class Scratch {
public static void main(String[] args) {
//List<String> list = Arrays.asList("a","b","c");
//list.forEach(System.out::println);
List<String> list = new ArrayList<>();
for(int i=1;i<=3;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
}, String.valueOf(i)).start();
}
//java.util.ConcurrentModificationException
//ArrayList 并发常见的异常
}
}
ArrayLIst的add方法没有加锁,所以有可能报并发异常java.util.ConcurrentModificationException ,这是ArrayList 并发常见的异常。
2. 导致原因
并发争抢修改导致,一个线程正在写,另一个线程来抢夺,导致数据不一致异常,并发修改异常。
3. 解决方案
① 改为new Vector<>();
List<String> list = new Vector<>();
vector是JDK1.0版本的List实现,add方法加synchronized,牺牲了并发性。
②使用synchronizedList
List<String> list = Collections.synchronizedList(new ArrayList<>());
注:Java中Collection和Collections的区别
Collection 是一个集合接口(集合类的一个顶级接口)它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现,其直接继承接口有List与Set。
Collections 则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
(具体见https://www.cnblogs.com/cathyqq/p/5279859.html)
③使用CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
写时复制:并发时,当线程1使用list时,先拷贝一份list,扩容1,写完之后将原来的数组引用修改为拷贝的数组,并返回true。
Set案例
Set也是线程不安全的
1. 故障现象
class Scratch {
public static void main(String[] args) {
//List<String> list = Arrays.asList("a","b","c");
//list.forEach(System.out::println);
Set<String> set = new HashSet<>();
for(int i=1;i<=30;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
}, String.valueOf(i)).start();
}
//java.util.ConcurrentModificationException
//ArrayList 并发常见的异常
}
}
Set也会报错:ConcurrentModificationException。
2. 解决方法
①使用synchronizedSet
Set<String> set = Collections.synchronizedSet(new HashSet<>());
②使用CopyOnWriteArrayList
Set<String> set = new CopyOnWriteArraySet<>();
另注: HashSet的底层是HashMap。HashSet的key是HashMap的key,HashSet的value都等于PRESENT(Object的常量)。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
而PRESENT是一个Object的常量。
private static final Object PRESENT = new Object();
Map案例
Map也是线程不安全的
1. 故障现象
class Scratch {
public static void main(String[] args) {
//List<String> list = Arrays.asList("a","b","c");
//list.forEach(System.out::println);
Map<String,String> map = new HashMap();
for(int i=1;i<=10;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
}, String.valueOf(i)).start();
}
//java.util.ConcurrentModificationException
//ArrayList 并发常见的异常
}
}
ArrayList、Set、Map抛出的异常一样,说明三者的底层是同一套体系。
2. 解决方法
①使用ConcurrentHashMap
Map<String,String> map = new ConcurrentHashMap();
①使用Collections.synchronizedMap
Map<String,String> map = new Collections.synchronizedMap(new HashMap<>());