我是靠谱客的博主 甜蜜小刺猬,最近开发中收集的这篇文章主要介绍线程安全版的ArrayList的性能比较--Collections.synchronizedList与CopyOnWriteArrayList,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

          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<String> arrayList = Collections.synchronizedList(new ArrayList<String>());
private static List<String> copyOnWriteArrayList = new CopyOnWriteArrayList<String>();
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<size;i++) {
arrayList.get(i);
}
cdl2.countDown();
}
}
//CopyOnWriteArrayList写线程
static class CopyAddThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<50000;i++) {
copyOnWriteArrayList.add(String.valueOf(i));
}
cdl3.countDown();
}
}
//CopyOnWriteArrayList写线程
static class CopyGetThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
int size = copyOnWriteArrayList.size();
for(int i=0;i<size;i++) {
copyOnWriteArrayList.get(i);
}
cdl4.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
long start1 = System.currentTimeMillis();
new ArrayAddThread().start();
new ArrayAddThread().start();
cdl1.await();
long end1 = System.currentTimeMillis();
System.out.println("ArrayList写操作时间:"+(end1-start1));
long start3 = System.currentTimeMillis();
new CopyAddThread().start();
new CopyAddThread().start();
cdl3.await();
long end3 = System.currentTimeMillis();
System.out.println("CopyOnWriteArrayList的写操作时间:"+(end3-start3));
long start2 = System.currentTimeMillis();
new ArrayGetThread().start();
new ArrayGetThread().start();
cdl2.await();
long end2 = System.currentTimeMillis();
System.out.println("ArrayList读操作时间:"+(end2-start2));
long start4 = System.currentTimeMillis();
new CopyGetThread().start();
new CopyGetThread().start();
cdl4.await();
long end4 = System.currentTimeMillis();
System.out.println("CopyOnWriteArrayList的读操作时间:"+(end4-start4));
}
}

测试结果如下:

ArrayList写操作时间:30
CopyOnWriteArrayList的写操作时间:5710
ArrayList读操作时间:28
CopyOnWriteArrayList的读操作时间:2

       从以上结果可以看出,Collections.synchronizedList同步化的ArrayList写操作使用了同步锁,明显比CopyOnWriteArrayListsh用锁加复制数组的速度快。同时,CopyOnWriteArrayList的读操作的速度更快,并发性更好。

对于add方法,Collections.synchronizedList()方法获取的对象,源码如下:

public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> 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方法创建的对象。

最后

以上就是甜蜜小刺猬为你收集整理的线程安全版的ArrayList的性能比较--Collections.synchronizedList与CopyOnWriteArrayList的全部内容,希望文章能够帮你解决线程安全版的ArrayList的性能比较--Collections.synchronizedList与CopyOnWriteArrayList所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(91)

评论列表共有 0 条评论

立即
投稿
返回
顶部