AVA线程安全集合Collection解析
按照方式分类
- 直接对每个操作加锁
- 读写分离,读共享,写加锁
按照实现方式分类
- 直接在方法上使用synchronized加锁
- 使用代理的方式对线程安全不安全的集合变为线程安全的
- 写的时候吧原有的数据复制出来、写完之后在赋值回去
JAVA中线程安全的Collection
Vector
直接对每个操作加锁、直接在方法上使用synchronized加锁
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
Vector的整体实现和上一篇说道的ArrayList相差不多,最大的差距就是扩容的时候,Vector可以指定扩容的大小,如果不指定,就是默认翻一倍。
CopyOnWriteArrayList
读写分离、写的时候吧原有的数据复制出来、写完之后在赋值回去
public boolean add(E e) {
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 获取原有的数据
Object[] elements = getArray();
int len = elements.length;
// 复制一个数据容量增加1的数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
// 新的数据赋值给老的数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
关键点
CopyOnWriteArrayList的最底层数组使用volatile关键字修饰来保证可见性,当数组改变后直接可以被其他线程访问到。
private transient volatile Object[] array;
缺点
如果集合中的数据过大,每进行一次写的操作就会复制出一份相同大小的数组,而原有的数据,是访问不可达(可达性分析),判定为可以被回收的内存,可能会导致大量的young gc,再假设内存的内容过大,创建的时候直接放在老年代,那么会产生大量的Full GC。
额外知识点
volatile关键字如果修饰数组,那么当只修改一个下标的元素时,不能保证可见性。
CopyOnWriteArraySet
CopyOnWriteArraySet的实现是基于CopyOnWriteArrayList所实现的,只是加了一个去重。
底层实现
private final CopyOnWriteArrayList<E> al;
底层是使用一个CopyOnWriteArrayList作为最基础的数据结构。在CopyOnWriteArraySet的API中套了一次,类似于装饰器,再添加元素的时候进行去重操作。
如何实现去重操作
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
// 已存在插入失败、不存在进行插入
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
/**
* 查找是否已经存在该元素
**/
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
Collections.synchronizedCollection()
使用代理的方式对线程安全不安全的集合变为线程安全的
实现原理
把一个线程不安全的线程代理为一个线程安全的线程,其中的主要类是java.util.Collections.SynchronizedCollection(代理类)。
创建
public static <T> Collection<T> synchronizedCollection(Collection<T> c) {
return new SynchronizedCollection<>(c);
}
创建时,把集合实体通过构造方法传入到SynchronizedCollection中。
加锁方式
SynchronizedCollection代理类中在执行目标对象的方法时,加了一个synchronized。其中mutex是可以指定的,如果不指定,那么默认是实体类本身。
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}