参考链接:
聊聊并发-Java中的Copy-On-Write容器
Java并发编程与技术内幕:CopyOnWriteArrayList、CopyOnWriteArraySet源码解析
前言:CopyOnWriteArrayList和CopyOnWriteArraySet 感觉非常简单,其中CopyOnWriteArraySet是基于CopyOnWriteArrayList实现的,只有add的方法稍微有些不同,因为CopyOnWriteArraySet是Set也就是不能有重复的元素,故在CopyOnWriteArraySet中用了addIfAbsent(e)这样的方法。
1.CopyOnWriteArrayList
顾名思义 就是复制一个数组,对复制后产生的新数组进行操作,而旧的数组不会有影响,所以旧的数组可以依旧就行读取(可以看出来,读的时候如果有新的数据正在写是无法实时的读取到的,有延时,得等新数据写完以后,然后才可以读到新的数据)
CopyOnWriteArrayList add (需要锁)
如果多个线程同时去写,多线程写的时候会Copy出N个副本出来,那么可能内存花销很大,所以用一个锁ReetrantLock锁住,一次只能一个线程去添加,这个线程是可重入的
public boolean add(T 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();
}
}
final void setArray(Object[] a) {
array = a;
}
CopyOnWriteArrayList get (需要锁)
get直接get即可,因为获取的旧的值所以没有影响。
2.CopyOnWriteArraySet
ADD
代码很简单,就是遍历去判断有没有,有的话直接返回false,没有的话就就赋值到新的数组,然后把新数组的引用返回即可。
public boolean add(E e) {
return al.addIfAbsent(e); //调用CopyOnWriteArrayList的addIfAbsent方法,如果数据重复返回false,不加入
}
//如果已存在就不放入
public boolean addIfAbsent(E e) {
final ReentrantLock lock = this.lock;
lock.lock();//取得锁
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = new Object[len + 1];
for (int i = 0; i < len; ++i) { //一个一个元素取出来进行对比
if (eq(e, elements[i]))
return false; // 跳出,list中已存在此元素
else
newElements[i] = elements[i]; //一个一个拷贝到新数组
}
newElements[len] = e;
setArray(newElements);//设置新数组
return true;//加入成功
} finally {
lock.unlock();//释放锁
}
}