由于ArrayList在多線程高并發情況下是不安全的,因此要慎用,那么此時如果涉及到集合操作,應該怎么選:
方案一:Vector:
特點:通過給所有方法都用?synchronized
?修飾從而保證線程安全,
缺點:CopyOnWriteArrayList
-
高并發場景下性能較差(鎖競爭嚴重)。
-
即使單線程環境也會因同步開銷影響性能。
總結:不建議使用。
方案二:Collections.synchronizedList
特點:低并發讀寫,簡單封裝?ArrayList
,所有方法加鎖。
缺點:高并必情況下性能不如CopyOnWriteArrayList
。
使用示例:
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 復合操作仍需外部同步
synchronized (syncList) {if (!syncList.contains("value")) {syncList.add("value");}
}
方案三:CopyOnWriteArrayList
特點:
? ? 1.
讀操作:無鎖,直接訪問底層數組;
? ? ? ? ? ? 2.寫操作:復制新數組,修改后替換舊數組(適合?讀多寫極少?的場景)。
缺點:
? ? ? ? ? ?1.寫操作開銷大(需復制數組)。
? ? ? ? ? ?2.數據一致性弱:讀取的是某一時刻的快照,可能讀到舊數據。
使用示例:
// 示例:事件監聽器列表
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
?拓展:
CopyOnwriteArrayList通過JUC包下的lock來實現線程間的同步的, 可實現了讀讀操作和讀寫操作不互斥。
它是怎么實現讀寫不互斥的呢?
在面臨寫操作的時候,CopyOnwriteArrayList會先復制原來的數組并且在新數組上進行修改,最后再將原數組覆蓋。如果寫操作過程中發生了線程切換。并且切換到讀線程,因為此時數組并未發生覆蓋,讀操作讀取的還是原數組。另外,數組定義private transient volatile Object[] array,其中采用volatile修飾,保證內存可見性,讀取線程可以馬上知道這個修改。也就是說當讀寫并發時讀操作是在舊數組中讀到的舊值(一致性弱)。
方案四:ConcurrentLinkedQueue
特點:
?
1.
高并發隊列操作(如任務分發),基于 CAS 無鎖實現。
? ? ? 2.線程安全:多線程并發添加無需額外同步。
? ? ? 3. 無阻塞:操作立即返回,不會因鎖競爭導致線程掛起。
? ? ? 4. 無容量限制:隊列會動態擴展。
缺點:
? ? ? ? 1.不支持隨機訪問(非?List
?接口實現)。
? ? ? ? 2.弱一致性:迭代器創建后,其他線程的修改可能不會立即反映到遍歷中。
? ? ? ??3.不支持?remove()
?操作:嘗試通過迭代器刪除元素會拋出?UnsupportedOperationException
使用示例:
public class Main {public static void main(String[] args) {ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();// 添加元素(推薦使用 offer)queue.offer("A"); // 返回 true 表示成功queue.add("B"); // 與 offer 等效// 讀取并移除頭部元素String head1 = queue.poll(); // 返回 "A",隊列變為 [B]System.out.println("Polled: " + head1);// 僅讀取頭部元素(不移除)String head2 = queue.peek(); // 返回 "B",隊列仍為 [B]System.out.println("Peeked: " + head2);// 隊列為空時queue.poll(); // 移除 "B",隊列為空 []String head3 = queue.poll(); // 返回 nullSystem.out.println("Polled empty: " + head3);}
}
方案五:ConcurrentHashMap
?
特點:可實現高性能隨機訪問,需要設置KEY.
方案對比:
開發推薦
現代開發中更推薦?CopyOnWriteArrayList
?
-
無鎖讀取:適合多核 CPU 環境,避免線程阻塞。
-
代碼簡潔:無需手動同步,減少錯誤。
-
安全迭代:迭代器不會拋出?
ConcurrentModificationException
。
但需注意其?內存占用(寫時復制會占用雙倍空間)。