JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

人生之路不会是一帆风顺的,我们会遇上顺境,也会遇上逆境,在所有成功路上折磨你的,背后都隐藏着激励你奋发向上的动机,人生没有如果,只有后果与结果,成熟,就是用微笑来面对一切小事。

导读:本篇文章讲解 JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

目录

 一、异常演示

二、解决方案

        1、vector 

        2、Collections工具类 

        3、CopyOnWriteArrayList 写时复制技术

三、写时复制技术     

        1 、特性

        2、原理 


 一、异常演示

        循环创建线程,将数据放入集合的同时,从集合中读取数据。

/**
 * list集合线程不安全问题
 */
public class ThreadDemo04 {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();


        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                //向集合中添加内容
                list.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

运行结果:

JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

        出现了并发修改异常 

        那么如何解决呢?

二、解决方案

        1、vector 

        直接将ArrayList替换为Vector即可 

//        List<String> list = new ArrayList<>();
        List<String> list = new Vector<>();

         运行结果:

JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

        为什么会这样呢?vector和ArrayList有什么不同呢?

        进入Vector的源码可以发现,里面几乎所有的方法都加了synchronized关键字

JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

        这样确实可以避免线程安全问题,不过效率比较低。

       2、Collections工具类 

        使用Collections工具类中的synchronizedList()方法生成线程安全的集合。 

//        List<String> list = new ArrayList<>();
//        List<String> list = new Vector<>();
        List<String> list = Collections.synchronizedList(new ArrayList<>());

        运行结果:

JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

         看一下jdk1.8的API对他的介绍:

JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

        3、CopyOnWriteArrayList 写时复制技术

//        List<String> list = new ArrayList<>();
//        List<String> list = new Vector<>();
//        List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();

        运行结果:

JUC学习(五):ArrayList的线程安全问题分析与解决方案(vector、Collections、写时复制技术)

三、写时复制技术     

        1 、特性

        它相当于线程安全的 ArrayList。和 ArrayList 一样,它是个可变数组;但是和 ArrayList 不同的时,它具有以下特性:

        1、它最适合于具有以下特征的应用程序:List 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
        2、 它是线程安全的。
        3、 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
        4、 迭代器支持 hasNext(), next()等不可变操作,但不支持可变 remove()等操作。
        5、 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。

        2、原理 

        写时复制技术中,读操作支持并发读,即多个线程可以同时读到集合内的元素。写操作是独立写,当一个写线程执行写操作时,所有其他写线程阻塞;当该线程写的时候,将原集合中的数据复制一份,在拷贝的集合中完成写操作后,再将该拷贝集合和原集合合并。

        为什么需要拷贝,在原数组直接修改不行吗?这篇文章讲得很清楚:CopyOnWriteArrayList写时复制的原理_Endwas的博客-CSDN博客

        最后我们来看一下源码:

    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();
        }
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/124915.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!