前言
对不安全集合类在高并发下解决方案的学习,予以记录!
一、不安全的集合类之ArrayList
面试:众所周知,arrayList是线程不安全的类,那么我们如何能让它在高并发情况下正常运行呢?
List list = new arrayList(); 新建一个empty list,初始默认大小10
ArrayList底层是一个对象数组,初始容量为10
public static void testArrayList() {
// 方法引用,consumer消费接口
List<String> arraysList = new ArrayList<>();
// arraysList.forEach(System.out::println);
for (int i = 1; i <= 80; i++) {
new Thread(() -> {
arraysList.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(arraysList);
}, String.valueOf(i)).start();
}
}
现象: 多线程下可能会出现Java.util.concurrentModificationException异常;
原因: 并发修改导致数据修改异常,参考花名册签名(多个人签名,签名册只有一份,当小强正在签的时候,小弱一把抽走签名册,导致产生了小强没有正常完成or签名错误等问题)
解决方案:
1、不考虑性能问题,使用vector类(add()等方法加synchronized)
List<Integer> list = new Vector<>();
2、不考虑性能问题,使用Collections集合工具类的方法synchronizedList(add()等方法加synchronized)
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
list.add(1);
3、使用JUC(java.util.concurrent)下的线程安全类CopyOnWriteArrayList(读写分离,写时复制)
List<Integer> copy = new CopyOnWriteArrayList<>();
copy.add(1);
具体原理及源码如下:
二、不安全的集合类之HashSet
面试:HashSet底层为hashmap,它的添加方法理应为hashmap.add(key,value),但是为什么hashset.add()只需要一个参数就可以,它的底层不是hashmap吗?
答:HashSet只关注他的key值,而不关心它的value值,也就是说,写入的参数就是key的值,而value则是一个使用final修饰的static的object 常量。
源码:
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// Dummy value to associate with an Object in the backing Map要与支持映射中的对象关联的伪值
private static final Object PRESENT = new Object();
public classDemo {
private static final Object PRESENT = new Object();
public static void main(String[] args) {
new HashSet<>().add(1);
System.out.println(PRESENT.toString());//java.lang.Object@45ee12a7指向一个object对象地址
}
}
解决方案:
1、不考虑性能问题,使用Collections集合工具类的方法synchronizedSet
Set set = Collections.synchronizedSet(new HashSet<>());
set.add(1);
2、使用JUC下的线程安全类CopyOnWriteArraySet
Set<Integer> arraySet = new CopyOnWriteArraySet<>();
arraySet.add(1);
三、不安全的集合类之HashMap
hashmap底层是一个Node<k, v>,默认容量16,默认加载因子0.75
private static void testHashMap() {
Map<String, String> map = new HashMap<>();
for (int i = 1; i <= 80; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
解决方案:
1、不考虑性能问题,使用Collections集合工具类的方法synchronizedMap
Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
map.put(1,1);
2、使用JUC下的线程安全类ConcurrentHashMap
Map<Object, Object> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(1,1);