什么是CopyOnWriteArrayList和CopyOnWriteArraySet
CopyOnWriteArrayList和CopyOnWriteArraySet都是Java并發編程中提供的線程安全的集合類。
CopyOnWriteArrayList是一個線程安全的ArrayList,其內部通過volatile數組和顯式鎖ReentrantLock來實現線程安全。它采用了一種稱為“寫入時復制”(Copy-On-Write)的策略,即當進行修改操作時,會先復制一份當前數據,然后在新的數據上進行修改,修改完成后再將指向原來數據的引用指向新的數據。這樣可以保證讀操作可以并發進行而不會被阻塞,同時也保證了數據的線程安全性。
CopyOnWriteArraySet是一個線程安全的Set,其內部實現是基于CopyOnWriteArrayList的。它持有一個CopyOnWriteArrayList的引用,所有的操作都是由這個CopyOnWriteArrayList來實現的。與CopyOnWriteArrayList不同的是,CopyOnWriteArraySet是無序的,并且不允許存放重復值。
由于CopyOnWriteArrayList和CopyOnWriteArraySet的寫操作都需要復制底層數組,因此如果集合較大或者寫操作非常頻繁,那么這種實現方式的性能可能會比較低。因此,在選擇使用這些集合類時需要根據具體的應用場景進行權衡。
另外,需要注意的是,雖然CopyOnWriteArrayList和CopyOnWriteArraySet的讀操作可以在不加鎖的情況下進行,但是并不是所有的讀操作都是完全無鎖的。例如,迭代器在迭代過程中會持有底層數組的引用,如果在這個過程中發生了寫操作,那么迭代器可能會拋出ConcurrentModificationException異常。因此,在使用迭代器進行遍歷時需要特別注意。
代碼示例
下面是使用CopyOnWriteArrayList和CopyOnWriteArraySet的簡單代碼示例:
CopyOnWriteArrayList示例
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListExample {public static void main(String[] args) {// 創建一個CopyOnWriteArrayListList<String> list = new CopyOnWriteArrayList<>();// 添加元素list.add("Element 1");list.add("Element 2");list.add("Element 3");// 在新的線程中修改列表new Thread(() -> {list.add("Element 4"); // 線程安全地添加元素}).start();// 主線程中迭代列表for (String element : list) {System.out.println(element); // 讀取操作也是線程安全的}}
}
CopyOnWriteArraySet示例
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;public class CopyOnWriteArraySetExample {public static void main(String[] args) {// 創建一個CopyOnWriteArraySetSet<String> set = new CopyOnWriteArraySet<>();// 添加元素set.add("Element 1");set.add("Element 2");set.add("Element 3");// 在新的線程中修改集合new Thread(() -> {set.add("Element 4"); // 線程安全地添加元素}).start();// 主線程中迭代集合for (String element : set) {System.out.println(element); // 讀取操作也是線程安全的}}
}
在上面的兩個示例中,CopyOnWriteArrayList和CopyOnWriteArraySet都是在多線程環境中安全使用的。即使在修改集合(添加元素)的同時,其他線程也可以安全地迭代集合。這是因為寫操作(如添加元素)會創建一個集合的新副本,并在修改完成后替換舊副本。這樣,讀取操作可以安全地繼續進行,而不會受到寫操作的干擾。
需要注意的是,由于寫操作需要復制整個底層數組,因此如果集合非常大,或者寫操作非常頻繁,性能可能會受到影響。在這種情況下,可能需要考慮使用其他并發集合,如ConcurrentHashMap的keySet或entrySet視圖,或者自定義的并發解決方案。