List的同步类比较
阅读原文时间:2023年07月08日阅读:1

TL;NRs

  • CopyOnWriteArrayList类在多线程顺序读取上有很大的优势,但在随机读取上反而有较大的劣势,且在写入方面性能极差。
  • Vector类在顺序读取方面性能较差,但在随机读取方面有较大的优势,写入方面性能也还可以。

1,引言

java线程安全的List实现有以下三种:

  1. new Vector<>()
  2. Collections.synchronizedList(new ArrayList<>())
  3. new CopyOnWriteArrayList<>()

通常认为使用了synchronized会导致运行变慢,那么在java针对synchronized进行一系列优化后,现在的情况如何呢?为了检验这一说法,写了一个验证程序进行验证。

2,验证代码

ArrayList作为基础,分别测试4种List的顺序写入(0 ~ 1 << 24)、顺序读取和随机读取,各十轮。据此编写代码。代码太长了,所以放到最后

3,测试平台

垃圾笔记本,使用Intel酷睿i5 7200U

java版本为java 12,HotSpot虚拟机

4,测试结果

单位:毫秒

5,结果分析

ArrayList(A)、Vector(V)、Collections.synchronizedList(new ArrayList<>())(S)、以及CopyOnWriteArrayList(C)四种类型的结果分别如下

十轮写入,单位毫秒

A

V

S

C

总时间

6426

9365

10186

inf

最大时间

1313

1016

1096

inf

最小时间

239

815

672

inf

十轮单线程顺序读,单位毫秒

A

V

S

C

总时间

41

2247

1538

1560

最大时间

22

418

200

167

最小时间

0

196

129

148

十轮单线程随机读,单位毫秒

A

V

S

C

总时间

2167

4908

11792

11133

最大时间

256

573

1372

1264

最小时间

202

473

1110

1030

十线程顺序读,单位毫秒

V

S

C

总时间

11232

12650

696

十线程随机读,单位毫秒

V

S

C

总时间

16828

17888

26089

6,结论

单线程写入性能:A > V = S >>>> C

单线程顺序读取性能:A >> S = C > V

单线程随机读取性能:A > V > S = C

20线程顺序读取性能:C >> V > S

20线程随机读取性能:V > S >> C

COW顺序读取性能较好,随机读取性能较差,写入性能极差。

Vector随机读取性能较好,顺序读取性能和写入性能较差。

附录 测试代码

import java.util.*;
import java.util.concurrent.*;

public class VectorTest {

    private static final int CNT = 1 << 24;
    private static final Random rand = new Random();

    public static void main(String[] args) throws InterruptedException {
        int writeRound = 10, readRound = 10, randomReadRound = 10;
        int nRead = 20, nRandomRead = 20;
        List<Integer> lsA = new ArrayList<>();
        List<Integer> lsV = new Vector<>();
        List<Integer> lsS = Collections.synchronizedList(new ArrayList<>());
        List<Integer> lsC = new CopyOnWriteArrayList<>();
        test(lsA, "ArrayList", writeRound, readRound, randomReadRound);
        test(lsV, "Vector", writeRound, readRound, randomReadRound);
        test(lsS, "SynArrayList", writeRound, readRound, randomReadRound);
        lsC.addAll(lsA);
        test(lsC, "COWList", 0, readRound, randomReadRound);
        multiThreadTest(lsV, "Vector", nRead, nRandomRead);
        multiThreadTest(lsS, "SynArrayList", nRead, nRandomRead);
        multiThreadTest(lsC, "COWList", nRead, nRandomRead);
    }

    private static void test(List<Integer> list, String name, int writeRound, int readRound, int randomReadRound) {
        int max = 0, min = Integer.MAX_VALUE, sum = 0;
        int[] w = new int[writeRound], r = new int[readRound], rr = new int[randomReadRound];
        for (int i = 0; i < writeRound; i++) {
            list.clear();
            int v = w[i] = writeTest(list);
            max = Math.max(max, v);
            min = Math.min(min, v);
            sum += v;
        }
        System.out.printf("%s write test: sum = %d, max = %d, min = %d\n", name, sum, max, min);
        for (int v : w) System.out.printf("%d\t", v);
        System.out.println();

        sum = max = 0;
        min = Integer.MAX_VALUE;
        for (int i = 0; i < readRound; i++) {
            int v = r[i] = readTest(list);
            max = Math.max(max, v);
            min = Math.min(min, v);
            sum += v;
        }
        System.out.printf("%s read test: sum = %d, max = %d, min = %d\n", name, sum, max, min);
        for (int v : r) System.out.printf("%d\t", v);
        System.out.println();

        sum = max = 0;
        min = Integer.MAX_VALUE;
        for (int i = 0; i < randomReadRound; i++) {
            int v = rr[i] = randomReadTest(list);
            max = Math.max(max, v);
            min = Math.min(min, v);
            sum += v;
        }
        System.out.printf("%s random read test: sum = %d, max = %d, min = %d\n", name, sum, max, min);
        for (int v : rr) System.out.printf("%d\t", v);
        System.out.println();
    }

    private static int writeTest(List<Integer> list) {
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < CNT; i++) list.add(i);
        long t1 = System.currentTimeMillis();
        return (int)(t1 - t0);
    }

    private static int readTest(List<Integer> list) {
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < CNT; i++) list.get(i);
        long t1 = System.currentTimeMillis();
        return (int)(t1 - t0);
    }

    private static int randomReadTest(List<Integer> list) {
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < CNT; i++) list.get(rand.nextInt(CNT));
        long t1 = System.currentTimeMillis();
        return (int)(t1 - t0);
    }

    private static List<Integer> ls;
    private static long t2 = 0;
    private static CountDownLatch cdl;

    public static class ThreadRead extends Thread {
        public void run() {
            for (int i = 0; i < CNT; i++) ls.get(i);
            long t1 = System.currentTimeMillis();
            t2 = Math.max(t1, t2);
            cdl.countDown();
        }
    }

    public static class ThreadRandomRead extends Thread {
        public void run() {
            for (int i = 0; i < CNT; i++) ls.get(rand.nextInt(CNT));
            long t1 = System.currentTimeMillis();
            t2 = Math.max(t1, t2);
            cdl.countDown();
        }
    }

    private static void multiThreadTest(List<Integer> list, String name, int nRead, int nRandomRead) throws InterruptedException {
        int tr = 0, trr = 0;
        ls = list;
        cdl = new CountDownLatch(nRead);
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < nRead; i++) {
            new ThreadRead().start();
        }
        cdl.await();
        tr = (int)(t2 - t0);

        cdl = new CountDownLatch(nRandomRead);
        t2 = 0;
        t0 = System.currentTimeMillis();
        for (int i = 0; i < nRandomRead; i++) {
            new ThreadRandomRead().start();
        }
        cdl.await();
        trr = (int)(t2 - t0);
        System.out.printf("%s: tr = %d, trr = %d\n", name, tr, trr);
    }
}