提到读写分离,大家可能首先会想到MySQL的读写分离,也就是在master节点上进行数据库写操作,在slave节点上进行数据库读操作,用这样的手段来提升数据库的性能、稳定性、高并发。其实,在java编程语言中,有一个集合类也贯彻了读写分离的思想,它就是:CopyOnWriteArrayList (另外一个类CopyOnWriteArraySet与此类似)。
一、CopyOnWriteArrayList总体介绍
我们先来看一下此集合类的层级结构:
![07b384ed997a1345a3a80fab96bce180.png](https://i-blog.csdnimg.cn/blog_migrate/aedad75880aebf867e2e5b38020942b3.jpeg)
在这里插入图片描述
CopyOnWriteArrayList类实现了List接口,是ArrayList一种线程安全的变体,所有的可变操作(增删改等),都是通过复制底层数组来进行的。官方文档说明如下:A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.
二、CopyOnWriteArrayList实现原理
通过源码看一下类的底层实现原理,以下是类变量:
//可重入锁,对所有可变操作加锁final transient ReentrantLock lock = new ReentrantLock();//存储数据的数组,保证可见性。只有getArray和setArray方法可以访问private transient volatile Object[] array;
向容器中添加元素的源码如下:
public boolean add(E e) { 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(); } }
可以看出,在开始添加元素前,先进行加锁操作,然后获取当前容器底层的数组数据,通过Arrays.copyOf方法复制到一个新的数组中,添加元素后把新的数组赋值给容器底层的数组,最后释放锁。其它的可变操作,比如删除、元素替换等,实现机制是相同的,都是通过复制一个新的数组来进行的。
读取操作比较简单,直接从数组中获取数据,没有进行加锁:
java public E get(int index) { return get(getArray(), index); }
因为写操作总是复制底层数组来进行运算,而读操作直接读取数组中数据,所以这是一种读写分离的形式。当然,因为每次可变操作都要进行数组的复制,这个代价是相当高的,所以CopyOnWriteArrayList比较适合读多写少的场合,比如商品品类,省市区等地址信息等。
三、与ArrayList的对比
既然CopyOnWriteArrayList是ArrayList的变体,那么它们肯定有区别,下面的表格从多个角度对比了两者不一样的地方:
![040c603a7815d483011029cf90748384.png](https://i-blog.csdnimg.cn/blog_migrate/6d08f4559686b02e550b307b4683b4e8.jpeg)
参考资料:http://www.benchresources.net/copyonwritearraylist-vs-arraylist-in-java/
http://www.makeinjava.com/copyonwritearraylist-concurrent-collection-java-example/
欢迎关注微信公众号【互联网全栈架构】,获取更多信息。
![712cf31d0ab4a914ad9a1bad8c550131.png](https://i-blog.csdnimg.cn/blog_migrate/7e2e13aecd15ab35161ca884f2ef34fb.jpeg)