之前帮一个朋友写代码的时候,遇到一个需要遍历列表。
一、单线程:性能较差
版本一、单线程方式
- Long startTime = System.currentTimeMillis();
-
- List<Long> sourceList = new ArrayList<>();
- for (long i = 0L; i < 1000L; i++) {
- sourceList.add(i);
- }
- System.out.println("原列表大小:" + sourceList.size());
-
- List<Long> resultList = new ArrayList<>();
- for (Long x : sourceList) {
-
- Long sum = 0L;
- for (long i = 0L; i < 3000000L; i++) {
- sum += x;
- }
- resultList.add(sum);
- }
- System.out.println("处理后的列表大小:" + resultList.size());
- System.out.println("耗时:" + (System.currentTimeMillis() - startTime) + "ms");
输出结果如下
二、多线程版本,不安全的 ArrayList
- Long startTime = System.currentTimeMillis();
- ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
-
- List<Long> sourceList = new ArrayList<>();
- for (long i = 0L; i < 1000L; i++) {
- sourceList.add(i);
- }
- System.out.println("原列表大小:" + sourceList.size());
- List<Long> resultList = new ArrayList<>();
- for (Long x : sourceList) {
- fixedThreadPool.execute(new Runnable() {
- @Override
- public void run() {
-
-
- Long sum = 0L;
- for (long i = 0L; i < 3000000L; i++) {
- sum += x;
- }
- resultList.add(sum);
- }
- });
- }
- fixedThreadPool.shutdown();
-
- while (!fixedThreadPool.isTerminated()) {
- }
- System.out.println("处理后的列表大小:" + resultList.size());
- System.out.println("耗时:" + (System.currentTimeMillis() - startTime) + "ms");
输出结果如下,速度提升了一半,随着处理越耗时,提升越明显

这里线程不安全主要原因是 resultList 这个对象的问题,ArrayList 是线程不安全的,主要原因是 add 的时候两个线程对同一个位置进行赋值,导致其中一个被覆盖了,也就丢失了。
线程安全的 List 有哪些呢?
synchronizedList()&CopyOnWriteArrayList
下面分别进行测试性能
三、多线程版本,线程安全,CopyOnWriteArrayList()方式
将版本二第11行的
List<Long> resultList =
new ArrayList<>();
替换为
List<Long> resultList = new CopyOnWriteArrayList<>();

多次尝试,处理前和处理后的都是1000,CopyOnWriteArrayList() 是线程安全的
四、多线程版本,线程安全,Collections.synchronizedList方式
将版本二第11行的
List<Long> resultList =
new ArrayList<>();
替换为
List<Long> resultList = Collections.synchronizedList(new ArrayList<>());

多次尝试,处理前和处理后的都是1000,Collections.synchronizedList 也是线程安全的
五、修改任务执行时间比较两种方式的性能
1.尝试通过修改任务时长比较多个List的性能
我们尝试修改版本二 中 17-23行的代码,如下
- Long sum = 0L;
- for (long i = 0L; i < 3000000L; i++) {
- sum += x;
- }
- resultList.add(sum);
尝试修改 3000000L
横坐标表示上面for循环次数 (其他的以版本二中的例子,其他不变)
时间单位:ms |
100万 |
200万 |
300万 |
400万 |
500万 |
1000万 |
2000万 |
ArrayList |
2374 |
4275 |
6081 |
8368 |
10224 |
19493 |
39275 |
CopyOnWriteArrayList |
2428 |
4429 |
6067 |
8108 |
10233 |
19421 |
37474 |
Collections.synchronizedList |
2284 |
4394 |
6267 |
8280 |
9888 |
19314 |
39229 |
比较了一番,可能例子不太恰当,导致结果发现这三个性能差不多
但是在我印象中,包括之前帮朋友写的代码中, Collections.synchronizedList 是优于 CopyOnWriteArrayList 的。
CopyOnWriteArrayList 的 add 方法性能其实是不太好的

2.尝试修改 原始List 的个数
- List<Long> sourceList = new ArrayList<>();
- for (long i = 0L; i < 1000L; i++) {
- sourceList.add(i);
- }
- System.out.println("原列表大小:" + sourceList.size());
即修改1000L
横坐标表示列表大小 (其他的以版本二中的例子,其他不变)
时间单位:ms |
100 |
200 |
400 |
800 |
1600 |
3200 |
6400 |
ArrayList |
1017 |
1534 |
2891 |
5136 |
9821 |
18503 |
36601 |
CopyOnWriteArrayList |
982 |
1475 |
2752 |
4930 |
9628 |
18329 |
36175 |
Collections.synchronizedList |
978 |
1667 |
2727 |
4929 |
9595 |
18709 |
37673 |
这里也看不出来三者性能的差别
3.尝试修改线程数
即修改
- ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
博主电脑是4核16GB的
横坐标是线程数 (其他的以版本二中的例子,其他不变)
时间单位:ms |
1 |
2 |
4 |
8 |
16 |
32 |
64 |
ArrayList |
10184 |
6891 |
6715 |
6725 |
6659 |
6479 |
6622 |
CopyOnWriteArrayList |
10111 |
6921 |
6299 |
6085 |
6651 |
6434 |
5955 |
Collections.synchronizedList |
9893 |
6570 |
6348 |
6227 |
6359 |
6206 |
6561 |
目前只能看出多线程比单线程快很多的,随着线程数增多会占用更多CPU,线程上下文切换也更加频繁。但是数据量太少,没有很好的体现。
本次只是介绍了有两种线程安全的 List,但是这两者性能还没比较出来,下次有机会再换一些测试用例比较吧。
您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏