集合类线程不安全问题
一、ArrayList
-
ArrayList底层是封装了数组,通过数组来存储数据的。当新建一个无参数的ArrayList时,new ArrayList() 的初始容量,在jdk1.6中的确是为10,然而在1.8中,如果只是new ArrayList() ,容量其实是0,当第一次通过add(E e)时,才扩充为10。
-
当ArrayList增加元素时,它是按照顺序从头部开始往后添加,它是有顺序的。
-
.果当添加的元素超过当前数组的长度时,它会新创建一个数组,长度为当前数组的1.5倍,然后将当前数组的元素复制到新的数组,当前数组的内存被释放
优点:操作读取操作效率高,基于数组实现的,可以为null值,可以允许重复元素,有序,异步。
缺点:由于它是由动态数组实现的,不适合频繁的对元素的插入和删除操作,因为每次插入和删除都需 要移动数组中的元素。
原因:线程不安全,add方法没有用synchronized修饰。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 1.List<String> list = new Vector();
//2.List<String> list = Collections.synchronizedList(new ArrayList<>());
//3.List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 20; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
1.故障现象
java.util.ConcurrentModificationException 并发修改异常
2.导致原因
并发修改异常。 一个线程在写,另外线程进行抢夺,导致数据不一致异常。
3.解决方案
(1) new Vector()
(2) Collections.synchronizedList(new ArrayList<>())
(3) new CopyOnWriteArrayList<>(); 写时复制
CopyOnWrite容器即写时复制容器,往一个容器object[]中添加元素的时候, 先copy出一个新容器object[] newElements,再往新的容器里添加这个新的元素, 再将原容器的引用指向新的容器 setArray(newElements);
但是在添加这个数据的期间,其他线程如果要去读取数据, 仍然是读取到旧的容器里的数据。 这样做的好处是可以对CopyOnWrite容器并发的读,而不用加锁, 因为当前容器不会添加任何元素,所以CopyOnWrite也是读写分离的思想,读和写不同的容器。
二、HashSet
HashSet概述和实现
- HashSet 底层是HashMap 初始值16,加载因子为0.75。
- 在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT = newObject(); (定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。)
- Set不能有重复的元素,HashMap不允许有重复的键。
HashSet插入
当有新值加入时,底层的HashMap会判断Key值是否存在,如果不存在,则插入新值,同时这个插入的细节会依照HashMap插入细节;如果存在就不插入。
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//和ArrayList 一样的两种方式保证 线程安全
// 1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
//2. Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
三、HashMap
HashMap与ConcurrentHashMap工作原理和区别
ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment(段),默认16个(concurrency level),然后每次操作对一个segment(段)加锁,避免多线程锁的几率,提高并发效率。
具体可以查看 HashMap和ConcurrentHashMap区别
public static void main(String[] args) {
Map<String,String> map = new HashMap<>(); //线程不安全
Map<String,String> ma1 = new ConcurrentHashMap<>(); //线程安全
for (int i = 0; i < 20; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
侵权请告之,立马删除。