探究List的报错信息java.lang.UnsupportedOperationException

一、场景说明

存在多个线程给一个List添加整型内容,所有进程执行结束后,打印出List的排序结果

二、CopyOnWriteArrayList

如果要实现上述场景,那么会先想到用CopyOnWriteArrayList这个集合作为List,这里简单介绍一下CopyOnWriteArrayList

使用ArrayList会出现并发问题,因为在多线程访问时没有锁,这个时候就可以考虑使用CopyOnWriteArrayList。

从名字上可以看出,这个List在做写时复制,原理是,当需要添加元素时,把原有的元素复制一份出来。

在写少读多的情况下,使用CopyOnWriteArrayList不仅能解决容器并发问题,而且效率会很高。CopyOnWriteArrayList效率高还体现在另外一方面,在写的时候不加锁。

三、问题复现

1.缺陷代码

下面这个代码是在jdk6环境下实现的“多线程添加内容后打印输出List排序结果”,代码可以这样来理解:
1.首先创建一个可以起100个线程类型的数组
2.每个线程都在做“添加100个0~1000以内的随机数”
3.每个线程在添加元素内容完毕后进行排序
4.最终打印排序后的list结果

package com.inspire.pro;

import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;

public class Test2 {
    public static void main(String[] args) {
        final List<Integer> list = new CopyOnWriteArrayList<Integer>();
        final Random r = new Random();
        Thread[] threads = new Thread[100];//100
        for (int i = 0; i < threads.length; i++) {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {//1000
                        list.add(r.nextInt(1000));//nextInt(int n) 该方法的作用是生成一个随机的int值,该值介于[0,n)的区间。
                    }
                    Collections.sort(list);
                }
            };
            threads[i] = new Thread(task);
        }
        runAndComputeTime(threads);
        System.out.println(list.size());
        System.out.println(list);

    }

    public static void runAndComputeTime(Thread[] threads) {
        long s1 = System.currentTimeMillis();
        for(int i=0;i<threads.length;i++){
            threads[i].start();
        }
        for(int i=0;i<threads.length;i++){
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long s2 = System.currentTimeMillis();
        System.out.println(s2 - s1 + "ms");
    }
}

2.运行结果

抛出错误信息:java.lang.UnsupportedOperationException

在这里插入图片描述

四、原因分析

从这个报错名字上来理解是:不支持操作异常。我们看一下第三行的错误堆栈,点进源码,指向这里的set抛出的异常

在这里插入图片描述

也就是说,我们传入的CopyOnWriteArrayList他的迭代器是不支持set方法的,是真的吗?那我们再追一下源码进行验证。点击116行的listIterator(),使用快捷键ctrl+alt+B查看实现了listIterator接口的方法,找到CopyOnWriteArrayList

在这里插入图片描述

点击去,发现这个接口返回了CopyOnWriteArrayList的迭代器COWIterator

在这里插入图片描述

点进这个类,找到set方法,CopyOnWriteArrayList是否支持set方法一看便知

在这里插入图片描述

果然,CopyOnWriteArrayList的迭代器是不支持set方法的(源码翻译:依旧;对于这个迭代器是不支持set方法的)
那么结论就是:Collections.sort(List)不支持对CopyOnWriteArrayList进行排序

五、代码改进

改进后的代码如下

变化点就是将原来的Collections.sort(list);替换成

List lineList = Arrays.asList(list.toArray());
Collections.sort(lineList);
list.clear();
list.addAll(lineList);

代码理解:
1.先将CopyOnWriteArrayList转成ArrayList
2.使用支持ArrayList的Collections的sort方法进行排序,当然,排序后的结果还是保存在lineList
3.对CopyOnWriteArrayList做清空操作
4.将排序好的lineList元素添加到CopyOnWriteArrayList中

完整代码如下

package com.inspire.pro;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;

public class Test2 {
    public static void main(String[] args) {
        final List<Integer> list = new CopyOnWriteArrayList<Integer>();
        final Random r = new Random();
        Thread[] threads = new Thread[100];//100
        for (int i = 0; i < threads.length; i++) {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {//1000
                        list.add(r.nextInt(1000));
                    }
                    //Collections.sort(list);//Collections.sort(List)不支持对CopyOnWriteArrayList进行排序
                    List lineList = Arrays.asList(list.toArray());
                    Collections.sort(lineList);
                    list.clear();
                    list.addAll(lineList);
                }
            };
            threads[i] = new Thread(task);
        }
        runAndComputeTime(threads);
        System.out.println(list.size());
        System.out.println(list);

    }

    public static void runAndComputeTime(Thread[] threads) {
        long s1 = System.currentTimeMillis();
        for(int i=0;i<threads.length;i++){
            threads[i].start();
        }
        for(int i=0;i<threads.length;i++){
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long s2 = System.currentTimeMillis();
        System.out.println(s2 - s1 + "ms");
    }
}

end

在这里插入图片描述

*过程中如果有问题可以关注微信公众号“程序艺术室”,添加我的微信向我咨询
*一健三连哦!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Inspiration666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值