博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK源码分析(10) CopyOnWriteArrayList
阅读量:5368 次
发布时间:2019-06-15

本文共 3964 字,大约阅读时间需要 13 分钟。

概述

CopyOnWriteArrayList是一个线程安全集合,原理简单说就是:在保证线程安全的前提下,牺牲掉写操作的效率来保证读操作的高效。所谓CopyOnWrite就是通过复制的方式来完成对数据的修改,在进行修改的时候,复制一个新数组,在新数组上面进行修改操作,这样就保证了不改变老数组,也就没有一写多读数据不一致的问题了。

定义

public class CopyOnWriteArrayList
implements List
, RandomAccess, Cloneable, java.io.Serializable

属性

一个是Lock,另一个是一个对象数组。

/** The lock protecting all mutators */    final transient ReentrantLock lock = new ReentrantLock();    /** The array, accessed only via getArray/setArray. */    private transient volatile Object[] array;

初始化

CopyOnWriteArrayList的初始容量是0,分为这样的几个步骤:

/**     * Creates an empty list.     */    public CopyOnWriteArrayList() {        setArray(new Object[0]);    }/**     * Sets the array.     */    final void setArray(Object[] a) {        array = a;    }

需要说明的是另一个有参构造方法,参数可以是一个集合

/**     * Creates a list containing the elements of the specified     * collection, in the order they are returned by the collection's     * iterator.     *     * @param c the collection of initially held elements     * @throws NullPointerException if the specified collection is null     */    public CopyOnWriteArrayList(Collection
c) { Object[] elements; 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) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); }

方法

add(E e)

/**     * Appends the specified element to the end of this list.     *     * @param e element to be appended to this list     * @return {@code true} (as specified by {@link Collection#add})     */    public boolean add(E e) {        // 锁 1.5 版本的锁 已经不用synchronizated        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();        }    }

从add方法中我们可以看到所谓的CopyOnWrite是如何实现的,在需要修改的时候,复制一个新数组,在新数组上修改,修改结束取代老数组,这样保证了修改操作不影响老数组的正常读取,另修改操作是加锁的,也就是说没有了线程不安全的问题。

和ArrayList相比较,效率比较低,只添加一个元素的情况下(初始容量均为0),用时是ArrayList的5倍左右,但是随着CopyOnWriteArrayList中元素的增加,CopyOnWriteArrayList的修改代价将越来越昂贵。

除了添加其他的修改操作也都是这样的套路,不做过多解释,如remove,也是加锁,复制新数组。

/**     * Removes the element at the specified position in this list.     * Shifts any subsequent elements to the left (subtracts one from their     * indices).  Returns the element that was removed from the list.     *     * @throws IndexOutOfBoundsException {@inheritDoc}     */    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 {                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();        }    }

get

public E get(int index) {  return get(getArray(), index);}//按照下标获取数组中对应的元素private E get(Object[] a, int index) {  return (E) a[index];}

读取的方法就很简单了,按照下标获取对应的元素。

总结

  1. 读写分离,我们修改的是新数组,读取的是老数组,不是一个对象,实现了读写分离。这种技术数据库用的非常多,在高并发下为了缓解数据库的压力,即使做了缓存也要对数据库做读写分离,读的时候使用读库,写的时候使用写库,然后读库、写库之间进行一定的同步,这样就避免同一个库上读、写的IO操作太多。
  2. 场景:读操作远多于修改操作

转载于:https://www.cnblogs.com/Tu9oh0st/p/10793507.html

你可能感兴趣的文章
职场上一个人情商高的十种表现
查看>>
【底层原理】深入理解Cache (下)
查看>>
Elasticsearch安装中文分词插件IK
查看>>
进阶4:常见函数-单行函数
查看>>
简述企业信息化与企业架构关系
查看>>
npoi List 泛型导出
查看>>
流程图怎么画?分享绘制流程图简单方法
查看>>
squid的处理request和reply的流程
查看>>
硬件_陀螺仪
查看>>
三、winForm-DataGridView操作——DataGridView 操作复选框checkbox
查看>>
SSIS的部署和配置
查看>>
计算机内存管理介绍
查看>>
POJ 2761 Feed the dogs 求区间第k大 划分树
查看>>
mysql中间件研究(Atlas,cobar,TDDL)[转载]
查看>>
ASP.NET应用程序与页面生命周期
查看>>
Linux--多网卡的7种Bond模式
查看>>
Oracle命令(一):Oracle登录命令
查看>>
业务建模 之 业务用例图
查看>>
EasyUI基础入门之Pagination(分页)
查看>>
一次PHP代码上线遇到的问题
查看>>