
JUC
集合类线程不安全
在多线程并发环境下,对集合类执行像
add( ),put( )这样的操作
会触发java.util.ConcurrentModificationException这样的异常上一篇已经举例了ArrayList的解决方法,这次看看其他集合类的底层到底是怎么解决的。
HashSet
1 | public class SetNotSafeDemo { |
使用
synchronizedSet来new一个HashSet,就可以解决报错的问题。1
2
3
4
5
6
7
8
9
10
11public class SetNotSafeDemo {
public static void main(String[] args) {
Set<String> stringSet = Collections.synchronizedSet(new HashSet<>());
for (int i = 0; i < 3; i++) {
new Thread(()->{
stringSet.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(stringSet);
},String.valueOf(i)).start();
}
}
}还可以使用
CopyOnWriteArraySet<E>这个类来初始化,也能解决这个问题1
2
3
4
5
6
7
8
9
10
11public class SetNotSafeDemo {
public static void main(String[] args) {
Set<String> stringSet = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
stringSet.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(stringSet);
},String.valueOf(i)).start();
}
}
}可以看一下CopyOnWriteArraySet的源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class CopyOnWriteArraySet<E> extends AbstractSet<E>
implements java.io.Serializable {
······
private final CopyOnWriteArrayList<E> al;
/**
* Creates an empty set.
*/
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
}可以发现CopyOnWriteArraySet在初始化的时候,还是new了一个
CopyOnWriteArrayList<E>
HashSet底层数据结构
就是HashMap!
下面是HashSet的构造函数。
注释上说了:构造一个新的、空的集合,后面的HashMap的实例有默认为16的容量和0.75的负载因子。
1 | /** |
但是对比HashSet和HashMap的添加方法会发现传的值是不一样的。
1 | Set<String> set = new HashSet<>(); |
既然底层都是HashMap,那为什么HashSet添加一个参数,HashMap要添加两个参数呢?
还是结合源码来学习
1 | public class HashSet<E> |
HashSet的add方法,其实调用的是Map的put方法,也是两个参数。只不过HashSetadd的就是HashMap中的Key,然后Value是一个叫PRESENT的常量
HashMap
1 | public class SetNotSafeDemo { |
跟其他的集合类一样,也会爆出ConcurrentModificationException。
解决方法:
使用
Collections.synchronizedMap(new HashMap());不要去JUC包下面找
CopyOnWriteHashMap。Java提供的解决方法是一个叫
ConcurrentHashMap<K,V>