目录
①:Collections.synchronizedSet();
①:Collections.synchronizedMap(new HashMap<>());
②:new ConcurrentHashMap(); //TODO 分析源码
一.集合类不安全之ArrayList
1.不安全代码演示
ArrayList<String> array = new ArrayList<String>();
//开启10个线程
for (int i=0;i<30;i++){
new Thread(()->{
array.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(array); //注:sout的array时需要走list的迭代器,只有边遍历边修改才会出现并发修改异常(重要)
},String.valueOf(i)).start();
}
2.异常现象
简单描述为:30个人在花名册签到,由于arraylist的add方法没有synchronized,一个同学在名字还未写完时被另一个同学抢走,导致数据不一致,并发修改异常。
3.导致原因
在输出打印array时(读)时,需要遍历list从而输出集合中的每一个元素,这时出现并发修改异常
4.解决方式
① Vector实现类,其add方法是加过锁的(数据一致性一定能得到保证,但并发性急剧下降),其与arraylist相比即“安全与效率的取舍”
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
② Collections.synchronizedList()
List<String> array = Collections.synchronizedList(new ArrayList<String>());
//开启10个线程
for (int i=0;i<30;i++){
new Thread(()->{
array.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(array);
},String.valueOf(i)).start();
}
③ juc包 CopyOnWriteArrayList类
List<String> array = new CopyOnWriteArrayList<>();
//开启10个线程
for (int i=0;i<30;i++){
new Thread(()->{
array.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(array); //sout的array需要走list的迭代器,只有边遍历边修改才会出现并发修改异常
},String.valueOf(i)).start();
}
其是一种读写分离的思想,共享读,写时将共享变量(花名册)拷贝,并扩容一个长度追加到末尾
源码分析:copyOnWriter容器即写时复制容器,往数组中添加元素时,不直接在elements数组中添加,而是先将elements 进行Copy复制出新容器newElements,这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite的读和写是不同的容器。所以适合读多写少的场景。
写:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
读:
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}
二.集合类不安全之HashSet
1.示例代码
HashSet<String> strings = new HashSet<>();
//开启10个线程
for (int i=0;i<30;i++){
new Thread(()->{
strings.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(strings); //sout的set需要走list的迭代器,只有边遍历边修改才会出现并发修改异常
},String.valueOf(i)).start();
}
2.异常现象
3.解决方式
①:Collections.synchronizedSet();
Set<String> strings = Collections.synchronizedSet(new HashSet<String>());
//开启10个线程
for (int i=0;i<30;i++){
new Thread(()->{
strings.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(strings); //sout的set需要走list的迭代器,只有边遍历边修改才会出现并发修改异常
},String.valueOf(i)).start();
}
②:new CopyOnWriteArraySet();
翻源码意外收获:
三.集合类不安全之Map
1.代码演示
HashMap<Object, Object> map = new HashMap<>();
//开启10个线程
for (int i=0;i<30;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map); //sout的map需要走map的迭代器,只有边遍历边修改才会出现并发修改异常
},String.valueOf(i)).start();
}