寫實複製原理(copy-on-write)
阿新 • • 發佈:2018-11-01
CopyOnWrite特點
讀寫併發時無需加鎖,一般用於讀多寫少的操作,但要注意的是,讀資料不能保證實時性
以CopyOnWriteArrayList原始碼進行分析
屬性
// 顯示操作的重入鎖物件
final transient ReentrantLock lock = new ReentrantLock();
// 用來儲存元素的物件陣列
private transient volatile Object[] array;
構造器
// 預設構造器 public CopyOnWriteArrayList() { setArray(new Object[0]); // 陣列長度為0 } // 給定一個Collection容器來構建CopyOnWriteArrayList例項 public CopyOnWriteArrayList(Collection<? extends E> 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); } // 給定一個數組來構建CopyOnWriteArrayList例項 public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); }
寫方法【寫時複製】
add(E e) - 新增一個元素
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); // =========== 新物件陣列 // 將元素新增到物件陣列的末尾 newElements[len] = e; // 替換內部物件陣列的引用 setArray(newElements); return true; } finally { // 解鎖 lock.unlock(); } }
add(int index, E element) - 新增一個元素到指定位置
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]; // =========== 新物件陣列 // 將elements下標為0開始的元素複製到newElements下標為[0,index)處 System.arraycopy(elements, 0, newElements, 0, index); // 將elements下標為index開始的元素複製到newElements下標為[index+1,numMoved)處 System.arraycopy(elements, index, newElements, index + 1,numMoved); } // 將element插入到newElements的index處 newElements[index] = element; // 替換內部物件陣列的引用 setArray(newElements); } finally { lock.unlock(); } }
set(int index, E element) - 替換指定位置的元素
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray(); // =========== 舊物件陣列
// index位置的舊元素
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();
}
}
讀方法
final Object[] getArray() {
return array; // 獲取內部物件陣列
}
// 根據下標獲取元素
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
使用測試
public class Main {
public static void main(String[] args) {
CopyOnWriteArrayList<String> cow = new CopyOnWriteArrayList<String>();
cow.add("a1");
cow.add("a2");
cow.add(1, "a1.5");
System.out.println("=====" + cow + "=====");
cow.set(1, "hehe");
System.out.println("=====" + cow + "=====");
/**
* output:
=====[a1, a1.5, a2]=====
=====[a1, hehe, a2]=====
*/
}
}