使用CopyOnWriteArrayList时建议使用foreach或iterator

导语

博客太久没更新了, 18年12月换了工作. 工作比较忙再加上自己有点贪玩. 看到了很多同学后台的提问和私聊. 找时间会统一回复的哈.

使用CopyOnWriteArrayList时建议使用foreach或iterator

关于CopyOnWriteArrayList就不细讲了. 网上资料一大堆. 这里只是提醒一种场景会导致使用CopyOnWriteArrayList时出现读取数据的异常情况. 即一条数据被读取了两次. 上代码

/******************************************************
 ****** @ClassName   : ArrayListTest.java                                            
 ****** @author      : milo ^ ^                     
 ****** @date        : 2018 11 16 13:00     
 ****** @version     : v1.0.x                      
 *******************************************************/
public class Test {
    public static void main(String[] args) {
        final CopyOnWriteArrayList list = new CopyOnWriteArrayList();
        list.add("元素1");
        list.add("元素2");

        new Thread(new AddThread(list)).start();
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i) + " 被main线程读取了.");
        }
    }

    static class AddThread implements Runnable{
        List list;

        AddThread(List list){
            this.list = list;
        }

        @Override
        public void run() {
            System.out.println("添加元素3 start");
            list.add(0,"元素3");
            System.out.println("添加元素3 end");
        }
    }
}

通过线程断点调试. 读取玩元素1后将线程挂起, 执行AddThread线程. 最终控制台输出

元素1 被main线程读取了.
添加元素3 start
添加元素3 end
元素1 被main线程读取了.
元素2 被main线程读取了.

原因分析

我们使用CopyOnWriteArrayList进行add操作的最后一步是将原引用替换为新数组(list数据结构是数组)地址.源码setArray(newElements),此时我们相当于在for循环执行过程中改变了list的数据.
执行第一次for循环时: list= [“元素1”,“元素2”]
执行第二次for循环时: list=[“元素3”,“元素1”,“元素2”]
所以造成我们分别在i=0和i=1时读到了两次元素1. 如果元素1的处理是个mq, 且下游没有做去重处理会直接导致实际业务问题.

解决方案

  1. 避免使用add(int index, E element)方法,改用add(E e)(业务允许情况下), 这样要么add操作没有完成执行原数组遍历操作, 要么已经完成且不会改变原数组0~(len-2)的数据(add方法是在数组尾部插入)
  2. 使用iterator或者foreach(基于iterator), 为什么iterator可以避免此问题. 看下源码. iterator是将数组拷贝了一份的.
    public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }

版权声明:本文为shangmingtao原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。