### CopyOnWriteArrayList
CopyOnWriteArrayList是juc中提供的并發安全的ArrayList,我們拆分一下類名"Copy""On""Write""ArrayList",從字面意思我們推斷出,這個是以在Write時進行Copy數組元素的ArrayList;
它主要具有一下特性:
* 它是線程安全的;
* 允許元素為null;
* 支持隨機訪問、淺拷貝,可序列化;
* 迭代器使用快照方式,且在迭代期間數組不會改變,故不會出現并發異常;
* 可變操作(set,add,remove,clear,replace,sort,sublist等等)的開銷很大,因為通常需要復制整個基礎數組;
* 所有的get方法只是獲取數組對應下標上的元素\(無需加鎖控制\)
CopyOnWriteArrayList使用了一種叫寫時復制的方法,當有新元素添加到CopyOnWriteArrayList時,先將原有數組的元素拷貝到新數組中,然后在新的數組中做寫操作,寫完之后,再將原來的數組引用(volatile修飾的數組引用)指向新數組;
CopyOnWriteArrayList提供了弱一致性的迭代器,保證在獲取迭代器后,其他線程對list的修改該不可見,迭代器遍歷時候的數組是獲取迭代器時候的一個快照;
CopyOnWriteArrayList是使用空間換時間的方式進行工作,它主要適用于讀多寫少,并且數據內容變化比較少的場景\(最好初始化時就進行加載數據到CopyOnWriteArrayList中\);
* **add\(E e\)**:添加元素
```
public boolean add(E e) {
/**
* 增加元素 e 到數組的末尾
* 操作步驟:
* 1. 獲取全局的 reentrantLock
* 2. 將原來的 array1 copy 到一個 array.length + 1 的數組 array2 里面
* 3. 將 先添加的元素e添加到新數組 array2 的最后一個空間里面 (array2[array2.length - 1] = e)
* 4. 將 新數組 array2 賦值給 CopyOnWriteArrayList 中的 array
*/
final ReentrantLock lock = this.lock;
lock.lock(); // 1. 獲取 全局 lock
try{
Object[] elements = getArray(); // 2. 獲取原來的數組
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1); // 3. 新建一個 array2 將原來的數據賦值到這個新建的數組里面
newElements[len] = e; // 4. 將 e 賦值給 array2的最后一個空間里面
setArray(newElements); // 5. 將新數組 array2 賦值給 CopyOnWriteArrayList 中的 array
return true;
}finally {
lock.unlock(); // 6. 釋放鎖
}
}
```
* **add\(int index, E element\):**添加元素到指定位置
```
public void add(int index, E element) {
/**
* 將元素 e 插入到數組 指定的索引下標 index 下
* 操作步驟:
* 1. 獲取全局的鎖
* 2. 獲取 CopyOnWriteArrayList 的 array, 及 array.length
* 3. 進行參數校驗 (index > len || index < 0) 則直接拋異常 -> 說明元素的插入只能在 0 - array.length 之間(包含兩個端點)
* 4. 獲取插入點 index 與 array.length 之間的步長, 進行分類討論
* 1) 插入的數據正好在 原array數組的后一個節點 (numMoved = len), 則直接新建一個 array, 將原來的 array copy 過來
* 2) 插入的 index 滿足 0 <= index <= len - 1, 則新建一個數組, 原來 o -> index(index不包含) 拷貝來, index后面的數據拷貝到新數組的 index + 1 的空間
* 5. 將 e 設置到 新 array 的 index 位置
* 6. 將 新 array 設置到 CopyOnWriteArrayList 里面
*/
final ReentrantLock lock = this.lock;
lock.lock(); // 1. 獲取全局的鎖
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){ // 走到這一步, 說明數據是插入到oldArray.length(這個值是指下標) 位置上的元素
newElements = Arrays.copyOf(elements, len + 1); // 直接拷貝原數組到一個新的 array 數組中, 這個數組的長度是 len + 1
}else{
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index); // 將原數組 index 前的數組都拷貝到新的數組里面
System.arraycopy(elements, index, newElements, index + 1, numMoved); // 將原數組 index 以后的元素都 copy到新的數組里面(包括index位置的元素)
}
newElements[index] = element; // 將 index 賦值 element
setArray(newElements); // 將 新的 array set到 CopyOnWriteArrayList 上
}finally {
lock.unlock();
}
}
```
* **remove\(int index\):**刪除指定位置元素
```
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){ // 說明刪除的元素的位置在 len - 1 上, 直接拷貝原數組的前 len - 1 個元素
setArray(Arrays.copyOf(elements, len - 1));
}else{
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index); // 拷貝原數組 0 - index之間的元素 (index 不拷貝)
System.arraycopy(elements, index + 1, newElements, index, numMoved); // 拷貝原數組 index+1 到末尾之間的元素 (index+1也進行拷貝)
setArray(newElements);
}
}finally {
lock.unlock();
}
return null;
```
- java演變
- JDK各個版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基礎
- 面向對象特性
- 多態
- 方法重載
- 方法重寫
- class
- 常量
- 訪問修飾符
- 類加載路徑
- java-equals
- 局部類
- java-hashCode
- Java類初始化順序
- java-clone方法
- JAVA對象實例化的方法
- 基礎部分
- JAVA基礎特性
- JAVA關鍵字
- javabean
- static
- 日期相關
- final
- interface
- 函數式接口
- JAVA異常
- 異常屏蔽
- try-with-resource資源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位運算符
- try-with-resource語法糖
- JDK冷知識
- JAVA包裝類
- JAVA基本類型與包裝類
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相關
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String編譯期優化
- StringBuilder&StringBuffer
- intern
- 注解
- java標準注解
- 內置注解
- 元注解
- 自定義注解
- 注解處理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 類型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA數組
- finalize方法
- JAR文件
- JAVA高級編程
- CORBA
- JMX
- SPI
- Java SPI使用約定
- ServiceLoader
- 實際應用
- IO
- 工具類
- JDK常用工具類
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 數據結構
- 棧-Stack
- 隊列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV鍵值對數據類型
- HashMap
- TreeMap
- Hash沖突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap結構
- jdk7&jdk8區別
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互轉換
- 線程安全的集合類
- 集合類遍歷性能
- 并發容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 設計模式
- 設計模式六大原則
- 單例模式
- 代理模式
- 靜態代理
- 動態代理
- JDK動態代理
- cglib動態代理
- spring aop
- 策略模式
- SpringAOP策略模式的運用
- 生產者消費者模式
- 迭代器模式
- 函數式編程
- 方法引用
- 性能問題
- Lambda
- Lambda類型檢查
- Stream
- findFirst和findAny
- reduce
- 原始類型流特化
- 無限流
- 收集器
- 并行流
- AOP
- 靜態織入
- aspect
- aspect的定義
- AspectJ與SpringAOP
- 動態織入
- 靜態代理
- 動態代理
- JDK動態代理
- CGLib動態代理
- Spring AOP
- SpringAOP五種通知類型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect優先級
- SpringAOP切點表達式
- within
- execution
- 嵌套調用
- 系統優化與重構
- 重疊構造器模式
- 工具類構造器優化
- 常見面試題
- new Object()到底占用幾個字節
- 訪問修飾符
- cloneable接口實現原理
- 異常分類以及處理機制
- wait和sleep的區別
- 數組在內存中如何分配
- 類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個模式
- 類的實例化順序
- 附錄
- JAVA術語
- FAQ
- 墨菲定律
- 康威定律
- 軟件設計原則
- 阿姆達爾定律
- 字節碼工具
- OSGI