目录
Collections.synchronizedList()
Collections.synchronizedList()
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
然后再往下边点,你会发现,下边都是类似于这种
public int size() {
synchronized (mutex) {return c.size();}
}
全部使用synchronized锁
使用CopyOnWriteArrayList类
我们要了解什么是cow(CopyOnWrite)
写入时复制技术就是不同进程在访问同一资源的时候,只有更新操作,才会去复制一份新的数据并更新替换,否则都是访问同一个资源。
了解了这一个简单的概念,那我们接着看源码就好理解了。
我们看更新操作的add源码
public boolean add(E e) {
//使用的是lock锁
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;//index最后加上这个元素
setArray(newElements); //新数组替换旧数组
return true;
} finally {
lock.unlock(); //解锁
}
}
我们很清楚的看到,添加操作不仅加锁,而且多占用了一份内存(复制了旧数组)。其他更新操作思想和这个类似,就不再赘述。
我们看读的操作get
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
//当前数组
final Object[] getArray() {
return array;
}
这个操作就简单明了了,没有加锁,就是简单的拿数组中的元素。
它的优缺点
我们就很容易看到他的优缺点,它支持并发操作,读写分离,最终一致,但是消耗了更多的内存空间,如果数组过大,占中内存过多,或许还会报错。
如何选择使用哪个
其实从他们的特点我们就可以看出来,Collections.synchronizedList()无论什么情况都加锁,CopyOnWriteArrayList只在更新操作加锁,消耗更多内存。
如果我们是读多更新少的情况,就用CopyOnWriteArrayList。如果是读和更新差不多就用Collections.synchronizedList(),最后还是要根据实际业务来确认的,相信你看到这里心里也就有数了。