zoukankan      html  css  js  c++  java
  • 浅析Java并发编程:如何做到线程安全-Collections.synchronizedList和CopyOnWriteArrayList的2种实现

      在多线程环境下ArrayList是线程不安全的,所以需要使用线程安全的List,我第一时间使用的是Collections.synchronizedList(new Arraylist<>()),但是在实际使用过程中却发生了安全事件。

    一、使用Collections.synchronizedList(new Arraylist<>()) + 同步解决线程安全问题

      当前存在一个类IntegerList,该类对象在多线程环境中添加元素和获取获取

    public class IntegerList {
    
        final private List<Integer> integerList;
    
        public IntegerList(List<Integer> integerList) {
            this.integerList = integerList;
        }
    
        /*
        * 获取列表中小于integer的所有数
        * */
        public List<Integer> getLessThanInteger(Integer integer){
            List<Integer> result=new ArrayList<>();
            for (Integer item : integerList) {
                if(item<=integer){
                    result.add(item);
                }
            }
            return result;
        }
    
        private void addInteger(Integer integer){
            integerList.add(integer);
        }
    
        @Override
        public String toString() {
            return "IntegerList{" +
                    "integerList=" + integerList +
                    '}';
        }
    }    

      使用ArrayList 线程不安全会报错ConcurrentModificationException

     public static void main(String[] args) {
            IntegerList integerList = new IntegerList(new ArrayList<>());
            for (int i = 0; i < 100; i++) {
                final int i1=i;
                new Thread(()->{
                    //添加元素
                    integerList.addInteger(i1);
                    //获取比i1小于等于所有数
                    List<Integer> lessThanInteger = integerList.getLessThanInteger(i1);
                }).start();
            }
        }

      这个是单线程也会报错的,原因是:添加的元素的同时,又获取元素,所以不安全。

      报错原因是在getLessThanInteger中使用增强for循环遍历了列表,集合的增强for循环是使用Iterator实现的,遍历过程中如果发现其他线程有修改列表就会抛异常

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dPYg9cl-1618477707391)(47C1CB1F99614FA1AA6696CA44A418A1)]

      编译后Class文件

      Iterator next()进行修改检查,如果集合被修改过,就会抛异常

      使用线程安全的Collections.synchronizedList 依然会报错

    public static void main(String[] args) {
            IntegerList integerList = new IntegerList( Collections.synchronizedList(new ArrayList<>()));
            for (int i = 0; i < 100; i++) {
                final int i1=i;
                new Thread(()->{
                    //添加元素
                    integerList.addInteger(i1);
                    //获取比i1小于等于所有数
                    List<Integer> lessThanInteger = integerList.getLessThanInteger(i1);
                }).start();
            }
        }

      为什么线程安全的Collections.synchronizedList在多线程下还是会报错?

      原因在于返回的迭代器是线程不安全的,在jdk源码里面注释说明,要用户自己使用同步来控制遍历迭代器

      使用同步改造:加入同步代码块,并且锁定的对象是 integerList

      这样就可以成功执行了。

    二、使用CopyOnWriteArrayList 完美解决迭代过程不安全问题

    public static void main(String[] args) {
            IntegerList integerList = new IntegerList(new CopyOnWriteArrayList<>());
            for (int i = 0; i < 100; i++) {
                final int i1 = i;
                new Thread(() -> {
                    //添加元素
                    integerList.addInteger(i1);
                    //获取比i1小于等于所有数
                    List<Integer> lessThanInteger = integerList.getLessThanInteger(i1);
                }).start();
            }
        }

      CopyOnWriteArrayList 解决线程安全的原因。CopyOnWriteArrayList 获取元素的时候使用的是当前数组Array的一个快照,而修改元素的时候,会复制一份Array再进行修改,修改不会对原本的Array产生影响,修改完后会覆盖原本的Array

      修改元素:

      迭代器:

      我们在使用线程安全的List应该使用CopyOnWriteArrayList,防止在遍历迭代器过程中出现异常,缺点是CopyOnWriteArrayList会使用额外的内存空间。

    原文链接:https://blog.csdn.net/piexie8364/article/details/115731723

  • 相关阅读:
    程序员版孔乙己
    痛!痛!痛!我们的好兄弟Git,一路走好!
    谈谈中台架构之交易中台
    一个单例还能写出花来吗?
    为什么数据库字段要使用NOT NULL?
    阿里二面:什么是mmap?
    退税不完全操作指南,我这样操作省了2000块!
    开源组件编排引擎LiteFlow发布里程碑版本2.5.0
    开源框架TLog核心原理架构解析
    对乱糟糟的日志说再见
  • 原文地址:https://www.cnblogs.com/goloving/p/15248504.html
Copyright © 2011-2022 走看看