ArrayList是线程不安全的,因此在并发编程时,经常会使用Collections.synchronizedList与CopyOnWriteArrayList来替代ArrayList,接下来对这两种list进行性能的比较。其中Collections.synchronizedLis在更新操作中使用了同步锁,而CopyOnWriteArrayList在更新操作中不仅使用了可重入锁,而且还需要进行数组的复制。以下是分别对两个同步类的插入和读取操作进行比较,比较两者的速度。
import java.util.*;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
/**
*
* @author tanhuiling
*Collections.synchronizedList和CopyOnWriteArrayList性能分析
*/
public class ListTest {
private static List arrayList = Collections.synchronizedList(new ArrayList());
private static List copyOnWriteArrayList = new CopyOnWriteArrayList();
private static CountDownLatch cdl1 = new CountDownLatch(2);
private static CountDownLatch cdl2 = new CountDownLatch(2);
private static CountDownLatch cdl3 = new CountDownLatch(2);
private static CountDownLatch cdl4 = new CountDownLatch(2);
//ArrayList写线程
static class ArrayAddThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<50000;i++) {
arrayList.add(String.valueOf(i));
}
cdl1.countDown();
}
}
//ArrayList读线程
static class ArrayGetThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
int size = arrayList.size();
for(int i=0;i
测试结果如下:
ArrayList写操作时间:30
CopyOnWriteArrayList的写操作时间:5710
ArrayList读操作时间:28
CopyOnWriteArrayList的读操作时间:2
从以上结果可以看出,Collections.synchronizedList同步化的ArrayList写操作使用了同步锁,明显比CopyOnWriteArrayListsh用锁加复制数组的速度快。同时,CopyOnWriteArrayList的读操作的速度更快,并发性更好。
对于add方法,Collections.synchronizedList()方法获取的对象,源码如下:
public static List synchronizedList(List list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
static class SynchronizedList
extends SynchronizedCollection
implements List {
private static final long serialVersionUID = -7754090372962971524L;
final List list;
SynchronizedList(List list) {
super(list);
this.list = list;
}
SynchronizedList(List list, Object mutex) {
super(list, mutex);
this.list = list;
}
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return list.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return list.hashCode();}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
public int indexOf(Object o) {
synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {
synchronized (mutex) {return list.lastIndexOf(o);}
}
public boolean addAll(int index, Collection extends E> c) {
synchronized (mutex) {return list.addAll(index, c);}
}
以上可以看出该对象通过互斥锁给list方法加锁从而封装了list的方法,使得该对象线程安全。
而CopyOnWriteArrayList则是先加可重入锁,然后使用数组复制的方法,每次将原数组复制到一个数组容量加1的新数组中,然后将当前添加元素添加到新数组尾部,从而实现插入。
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();
}
}
两种动态数组插入数据都使用了锁保证线程安全,但是CopyOnWriteArrayList还使用了数组复制的方法因此更耗时间,因此插入数据方面,Collections.synchronizedList性能更好。
对于get方法,Collections.synchronizedList()方法创建对象如上代码所示依旧使用了互斥锁,而CopyOnWriteArrayList代码如下:
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}
可以看出CopyOnWriteArrayList的get方法读取数据就如同普通数组读取某个元素一般,时间复杂度O(1)。因此在get方法中CopyOnWriteArrayList性能优于Collections.synchronized方法创建的对象。