• JUC——CopyOnWriteArrayList


    CopyOnWriteArrayList是java为了解决ArrayList并发容器提供的工具类,它通过对写操作加锁串行处理,当写操作在进行时,也不影响读操作;每次写操作都会复制一个副本出来,在副本没有指定真正的array引用之前,读请求可能会读到旧的数据,所以它比较适合读多写少,并且能够容忍最终一致性的问题;

    1. Arrays.copyOf和System.arraycopy

    Arrays.copyOf也是复制一份数组,它的内部实现也是基础System.arraycopy来实现的,System.arraycopy也是一种浅拷贝;
    接下来做一个小实验:

    Stu[] array = new Stu[3];
    array[0]=new Stu("0");
    array[1]=new Stu("1");
    array[2]=new Stu("2");
    
    Stu[] array2 = new Stu[3];
    System.arraycopy(array,0,array2,0,array2.length);
    Stu stu = array[0];
    stu.name="tom";
    System.out.println(array2[0]); // tom
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    说明System.arraycopy也仅仅是引用的copy;

    2. 构造方法

    • final transient ReentrantLock lock = new ReentrantLock(); 用来set/add加锁用的
    • private transient volatile Object[] array; 数据存储在array数组这里;
    // 构造一个长度为0的数组;
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
    // 传递一个Collection集合
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        // 直接将elements指定传递进来的CopyOnWriteArrayList的array
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            elements = c.toArray();
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elements.getClass() != Object[].class)
                // 进行copy
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }
    // 传入数组,则直接复制
    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3. add方法

     public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        // 写时加锁
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            // 新数组的长度要+1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            // 将E添加到数组尾部
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4. set方法

    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        // 写时加锁
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);
    
            // 如果旧值!=新值时,才进行set
            if (oldValue != element) {
                int len = elements.length;
                // 复制出来一个新数据
                Object[] newElements = Arrays.copyOf(elements, len);
                // 将指定index位置设置为新值
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    5. get

    public E get(int index) {
            return get(getArray(), index);
    }
    private E get(Object[] a, int index) {
            return (E) a[index];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6. remove

    public E remove(int index) {
            final ReentrantLock lock = this.lock;
            // 加锁
            lock.lock();
            try {
                Object[] elements = getArray();
                int len = elements.length;
                E oldValue = get(elements, index);
                // 需要移动的元素个数
                int numMoved = len - index - 1;
                if (numMoved == 0)
                    setArray(Arrays.copyOf(elements, len - 1));
                else {
                    // 新数组长度-1
                    Object[] newElements = new Object[len - 1];
                    // 复制删除位置前一半
                    System.arraycopy(elements, 0, newElements, 0, index);
                    // 复制删除位置之后元素
                    System.arraycopy(elements, index + 1, newElements, index,
                                     numMoved);
                    setArray(newElements);
                }
                return oldValue;
            } finally {
                lock.unlock();
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    后疫情时代,PCB+SMT制造业弯道超车:加速打造智能制造信息化4.0
    [附源码]java毕业设计企业人力资源管理系统
    R语言编写用户自定义函数:处理可能出错的代码、遇到错误继续处理而不停止代码运行
    mysq基础语句+高级操作(学这篇就够了)
    基于stm32F4的智能宠物喂食器的设计:LVGL界面、定时喂食喂水通风
    SpringMVC的视图
    设置pdb自动启动
    计算机毕业设计Java慧学IT精品课程网站(源码+系统+mysql数据库+lw文档)
    如何高效获取电商数据
    Django Test
  • 原文地址:https://blog.csdn.net/shixiaoling123/article/details/125462592