线程安全版的ArrayList的性能比较--Collections.synchronizedList与CopyOnWriteArrayList

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/carson0408/article/details/81836189

          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方法创建的对象。

猜你喜欢

转载自blog.csdn.net/carson0408/article/details/81836189