机制
CopyOnWriteArrayList:专为多线程并发设计的容器,“写入时复制”策略。
Collections.synchronizedMap:同步容器,独占策略。
代码:
- package test;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Date;
- import java.util.List;
- import java.util.Random;
- import java.util.concurrent.CopyOnWriteArrayList;
- /**
- * @author Alfred Xu <alfred.xu@heweisoft.com>
- *
- */
- public class ListPerformance {
- private final int threadNumber;
- public ListPerformance(int n) {
- threadNumber = n;
- }
- private static class TestThread implements Runnable {
- private static long totolTime;
- private final int No;
- private final int loop = 100000;
- private final Thread t;
- private final List<Integer> list;
- TestThread(int No, List<Integer> list) {
- this.No = No;
- this.list = list;
- t = new Thread(this);
- }
- public void start() {
- t.start();
- }
- public synchronized void addTime(long time) {
- totolTime += time;
- }
- @Override
- public void run() {
- long time = randomAccess();
- addTime(time);
- }
- @Override
- public String toString() {
- return "Thread " + No + ":";
- }
- public long randomAccess() {
- Date date1 = new Date();
- Random random = new Random();
- for (int i = 0; i < loop; i++) {
- int n = random.nextInt(loop);
- list.get(n);
- }
- Date date2 = new Date();
- long time = date2.getTime() - date1.getTime();
- // System.out.println(this + list.getClass().getSimpleName()
- // + " time:" + time);
- return time;
- }
- }
- public void initList(List<Integer> list, int size) {
- for (int i = 0; i < size; i++) {
- list.add(new Integer(i));
- }
- }
- public void test(List<Integer> list) {
- System.out.println("Test List Performance");
- TestThread[] ts = new TestThread[threadNumber];
- for (int i = 0; i < ts.length; i++) {
- ts[i] = new TestThread(i, list);
- }
- for (int i = 0; i < ts.length; i++) {
- ts[i].start();
- }
- }
- public static void main(String[] args) {
- ListPerformance lp = new ListPerformance(64);
- List<Integer> al = Collections
- .synchronizedList(new ArrayList<Integer>());
- lp.initList(al, 100000);
- lp.test(al);
- System.out.println(al.size());
- System.out.println(TestThread.totolTime);
- TestThread.totolTime = 0;
- CopyOnWriteArrayList<Integer> cl = new CopyOnWriteArrayList<Integer>(al);
- lp.test(cl);
- System.out.println(cl.size());
- System.out.println(TestThread.totolTime);
- }
- }
说明:
1.在System.out.println(al.size());和System.out.println(cl.size());处需要设置断点,让main线程暂停以等待访问线程(TestThread)运行完后获得正确结果。
2.总时间中未考虑JVM线程调度等花费的时间,这些时间远小于但访问线程的访问时间,可以忽略。
结果:
线程数 | 平均访问时间 | |
| CopyOnWriteArrayList | Collections.synchronizedMap |
2 | 20 | 100 |
4 | 25 | 500 |
8 | 50 | 2200 |
16 | 120 | 8000 |
32 | 200 | 30000 |
64 | 550 | 120000 |
*程序在每个线程数设置运行10次,取每次结果的前两位为有效值计算平均值。
分析:
可以看到随着线程数不断翻倍,CopyOnWriteArrayList的访问时间基本也是翻倍,但Collections.synchronizedMap的时间则是*4。在两个线程下Collections.synchronizedMap访问时间大概是CopyOnWriteArrayList的5倍,但在64线程的时候就变成了200倍+。所以如果在容器完全只读的情况下CopyOnWriteArrayList绝对是首选。但CopyOnWriteArrayList采用“写入时复制”策略,对容器的写操作将导致的容器中基本数组的复制,性能开销较大。所以但在有写操作的情况下,CopyOnWriteArrayList性能不佳,而且如果容器容量较大的话容易造成溢出。代码中如果CopyOnWriteArrayList cl按照ArrayList al的方法初始化就会造成溢出。