jdk源码分析之CopyOnWriteArrayList
来源:互联网 发布:知乎 记忆联想的方法 编辑:程序博客网 时间:2024/06/05 04:20
CopyOnWriteArrayList的原理
CopyOnWriteArrayList的核心思想是利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的数组,在新的数组上面修改,然后将新数组赋值给旧数组的引用,并通过volatile 保证其可见性,通过Lock保证并发写。
底层数据结构
private volatile transient Object[] array;final Object[] getArray() { return array;final void setArray(Object[] a) { array = a; }
底层采用Object数组存储数据
数组使用volatile修饰保证可见性,不读缓存直接读写内存
数组使用private修饰限制访问与,只能通过getter和setter访问
数组使用transient修饰,表示序列化时忽略此字段(自己定制序列化操作)
定制序列化操作
因为Object数组被transient修饰,因此需要CopyOnWriteArrayList类自己制定序列化方案
方法writeObject和readObject处理对象的序列化。如果声明该方法,它将会被ObjectOutputStream调用而不是采用默认的序列化方案。ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod,所以这些方法不得不被声明为priate以至于供ObjectOutputStream来使用。
在两个方法的开始处,调用了defaultWriteObject()和defaultReadObject()。它们做的是默认的序列化进程,就像写/读所有的non-transient和 non-static字段(但他们不会去做serialVersionUID的检查).通常说来,所有我们想要自己处理的字段都应该声明为transient。这样的话,defaultWriteObject/defaultReadObject便可以专注于其余字段,而我们则可为被transient修饰的字段定制序列化。
/** * Saves the state of the list to a stream (that is, serializes it). */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ s.defaultWriteObject(); Object[] elements = getArray(); // Write out array length s.writeInt(elements.length); // Write out all elements in the proper order. for (Object element : elements) s.writeObject(element); }
先调用s.defaultWriteObject()对非transient修饰的字段进行序列化操作
然后序列化写入数组的长度,再循环写入数组的元素
/** * Reconstitutes the list from a stream (that is, deserializes it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); // bind to new lock resetLock(); // Read in array length and allocate array int len = s.readInt(); Object[] elements = new Object[len]; // Read in all elements in the proper order. for (int i = 0; i < len; i++) elements[i] = s.readObject(); setArray(elements); }
反序列化的时候先调用s.defaultReadObject()恢复没有被transient修饰的字段
然后为反序列化得到的CopyOnWriteArrayList对象创建一把新锁
接着恢复数组的长度,根据数组的长度创建一个Object的数组
然后恢复数组的每一个元素
读操作不加锁
public E get(int index) { return get(getArray(), index); } private E get(Object[] a, int index) { return (E) a[index]; }
读操作是直接通过getArray方法获取Object数组,然后通过下标index直接访问数据。读操作并没有加锁,也没有并发的带来的问题,因为写操作是加锁写数组的副本,写操作成功将副本替换为原数据,这也是写时复制名字的由来。
加锁写副本
public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); E oldValue = get(elements, index); if (oldValue != element) { int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics setArray(elements); } return oldValue; } finally { lock.unlock(); } }
set方法先通过lock加锁,然后获取index位置的旧数据,供最后方法返回使用
E oldValue = get(elements, index);
接着创建数组的副本,在副本上进行数据的替换
Object[] newElements = Arrays.copyOf(elements, len);
Arrays.copyOf(elements, len)方法将会从elements数组复制len个数据创建一个新的数组返回
然后在新数组上进行数据替换,然后将新数组设置为CopyOnWriteArrayList的底层数组
newElements[index] = element;
setArray(newElements);
最后在finally块里边释放锁
特定位置添加数据
public void add(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+len); Object[] newElements; int numMoved = len - index; if (numMoved == 0) newElements = Arrays.copyOf(elements, len + 1); else { newElements = new Object[len + 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index, newElements, index + 1, numMoved); } newElements[index] = element; setArray(newElements); } finally { lock.unlock(); } }
添加数据和替换数据类似,先加锁,然后数组下标检查,接着创建数组副本,在副本里边添加数据,将副本设置为CopyOnWriteArrayList的底层数组
- jdk源码分析之CopyOnWriteArrayList
- JDK之CopyOnWriteArrayList源码解读
- jdk并发包 CopyOnWriteArrayList源码分析
- JDK源码解析之ArrayList与Vector与CopyOnWriteArrayList
- CopyOnWriteArrayList 源码分析
- Java CopyOnWriteArrayList 源码分析
- CopyOnWriteArrayList 源码分析
- CopyOnWriteArrayList源码原理分析
- CopyOnWriteArrayList源码分析
- 源码分析-CopyOnWriteArrayList
- CopyOnWriteArrayList源码分析
- CopyOnWriteArrayList-源码分析
- CopyOnWriteArrayList源码分析
- LinkedList&CopyOnWriteArrayList源码分析
- 【JDK】:CopyOnWriteArrayList、CopyOnWriteArraySet 源码解析
- Java concurrent Framework并发容器之CopyOnWriteArrayList(1.6)源码分析
- CopyOnWriteArrayList 源码阅读与分析
- 《Java源码分析》:CopyOnWriteArrayList/ CopyOnWriteArraySet
- 一起来学Android Studio:(二)Gradle
- CSDN-markdown编辑器
- 当运行pychrm时遇到please select a valid interpreter怎么解决
- linux bin和sbin区别
- u3d 学习笔记
- jdk源码分析之CopyOnWriteArrayList
- Synchronized和lock机制
- Spring线程池开发实战
- Eclipse中使用javap命令打出字节码虚指令
- ASP.NET之AJAX分页步骤
- web工程中各类地址的写法
- register_spine_module(lua_State*) in libluacocos2d iOS.a(lua_cocos2dx_spine_manual.o)
- c++静态成员变量问题
- [个人总结]职场进阶修炼