暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java高并发系列之CopyOnWriteArrayList源码解析

码农的修炼之道 2018-12-31
207

    我们知道,集合框架中大部分数据结构类都是单线程下的,不然HashMap、ArrayList等,今天来看一种线程安全的list,也就是CopyOnWriteArrayList。首先来看一下类图,如下:

    从类图可以看出,CopyOnWriteArrayList实现了RandomAccess接口,可以支持快速访问。也实现了List、Serializable接口等。此外,在其内部定义了一个ReentrantLock作为独占锁。


1、构造函数

     构造函数有三个,分别是无参构造函数,源码如下:

    无参数构造函数,默认创建一个空数组。其次,有参数的构造函数如下:

    此外,还有一种构造函数,可以传入collection。源码如下:

    上面的代码就是将collection中的元素存储到本地的数组中。


2、add操作

    add操作是向list中添加元素,其源码如下:

    上面的源码中,首先获取锁。然后调用getArray得到本地的数组。然后调用Arrays的copyof方法将array数组复制到新数组中,接着添加元素到新数组中。最后,调用setArray方法将新数组newElements代替之前的array。最终,释放锁。

    其他还有add(index,E)方法等,在某个位置插入元素。addAll方法,插入很多元素。这些原理都与add方法相似,不再叙述。


3、get操作

  get操作是获取指定下标的元素,其源码如下:

     其中,getArray方法返回本地数组。我们看看return调用的这个get方法的源码。

    这个get方法就是返回指定下标的元素。这里在方法上面标注了unchecked,也就是说明index需要调用者自己检查是否越界。也就是说数组越界,会报IndexOutBoundsException。


4、set操作

    set操作是设置指定下标的元素,也就是修改元素值,其源码如下:

    从上面的源码可以看出,首先肯定还是加锁,最后是否锁的操作。这样可以阻止其他线程对array数组的修改。这个代码首先获取oldValue值,如果oldValue和传入的参数element不相等,才进行修改。修改的过程和add元素相似,都是新建一个数组,然后将array数组拷贝出来,然后修改指定下标值,最后重新将新数组设置成本地的array数组。


5、remove操作

    remove操作是删除指定下标的元素,源码如下:

      从源码可以看出,首先加锁、最后释放锁。在try语句中,首先获取当前array数组,然后调用get方法获取指定下标元素oldValue。然后计算新数组的长度numMoved。

     如果numMoved为0,那么说明是删除最后一个元素,也就复制倒数第二个之前的元素即可。

    否则,不是删除最后一个元素,那么就分成两段赋值到本地newElements数组中,并设置为新数组即可。


6、size操作

    size操作是返回list中元素个数,由于底层依赖动态数组,所有这里直接返回数组的长度即可。


7、小结

     CopyOnWriteArrayList是基于写时复制的方式进行线程安全的,也就是说写操作的时候,会获取锁,然后创建本地数组newElements,修改后将这个newElements设置为新的数组即可。

    此外,CopyOnWriteArrayList中的迭代器是弱一致性的,也就是某个线程在迭代器输出的是当时获取的副本。其他线程如果该时刻修改了某个元素,迭代器那个线程不会输出最新的值的。这一点在使用上面需要注意

文章转载自码农的修炼之道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论