並行集合類別大多位於java.util.concurrent包中,包括ConcurrentHashMap、CopyOnWriteArrayList和各種BlockingQueue實現等。
原子操作則提供了一種無鎖的同步機制,允許在多執行緒環境中安全地更新共享變數,而無需使用顯式鎖定。
Java的java.util.concurrent.atomic包提供了一系列支援原子操作的類別,如AtomicInteger、AtomicLong等。
主要特點包括:
執行緒安全:並行集合內部實現了同步機制,可以安全地在多執行緒環境中使用,無需額外的同步處理。
高並行性:相比於使用synchronized關鍵字的傳統同步集合,並行集合採用更細粒度的鎖定策略或無鎖算法,允許多個執行緒同時訪問集合的不同部分。
效能優化:並行集合針對常見的多執行緒場景進行了優化,在高並發情況下通常能提供更好的效能。
Java中常見的並行集合包括:
使用並行集合的優勢:
然而,並行集合並非萬能的。在選擇使用時,需要考慮以下因素:
在接下來的章節中,我們將詳細介紹幾種常用的並行集合,並探討它們的特性和適用場景。
ConcurrentHashMap是Java並行程式設計中最常用的並行集合之一,它提供了執行緒安全的Map實現,並且在高並發環境下表現出色。
特性:
使用方法:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key1", 1);
map.put("key2", 2);
// 原子性操作
map.putIfAbsent("key3", 3);
map.replace("key1", 1, 10);
// 併合操作
map.merge("key1", 5, Integer::sum);
// 計算操作
map.compute("key4", (k, v) -> (v == null) ? 1 : v + 1);
// 批量操作
map.forEach((k, v) -> System.out.println(k + " = " + v));
優勢:
使用場景:
注意事項:
ConcurrentHashMap是一個強大的並行工具,適合在多執行緒環境中替代同步的HashMap或Hashtable。正確使用ConcurrentHashMap可以顯著提升應用程式的並行處理能力和整體效能。
CopyOnWriteArrayList是ArrayList的一個執行緒安全變體,專為處理讀多寫少的並發場景而設計。它的特點是在執行寫操作時,會複製整個底層陣列。
特性:
使用方法:
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("項目1");
list.add("項目2");
// 讀操作
String item = list.get(0);
// 寫操作
list.add("項目3");
// 迭代
for (String s : list) {
System.out.println(s);
}
// 批量操作
list.addAll(Arrays.asList("項目4", "項目5"));
優勢:
使用場景:
注意事項:
CopyOnWriteArrayList是一個在特定場景下非常有用的工具。通過犧牲寫操作的效能來換取讀操作和迭代的高效性,需要仔細評估應用場景的讀寫比例,以確保能夠帶來效能提升而不是損失。
BlockingQueue是Java並發包中的一個介面,擴展Queue介面,提供了阻塞的插入和獲取操作,非常適合用於生產者-消費者模式(Producer-Consumer Pattern)的實現。
BlockingQueue的主要特性:
常見的BlockingQueue實現:
ArrayBlockingQueue:
LinkedBlockingQueue:
PriorityBlockingQueue:
DelayQueue:
SynchronousQueue:
使用範例(以ArrayBlockingQueue為例):
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
// 生產者
new Thread(() -> {
try {
queue.put("任務");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消費者
new Thread(() -> {
try {
String task = queue.take();
processTask(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
使用場景:
選擇合適的BlockingQueue實現時,需要考慮以下因素:
ConcurrentSkipListMap和ConcurrentSkipListSet是Java並發包中提供的兩個重要的並行集合類別,分別實現ConcurrentNavigableMap和ConcurrentNavigableSet介面。
這兩個類別基於Skip List(跳表)資料結構實現。
特性:
ConcurrentSkipListMap使用範例:
ConcurrentSkipListMap<String, Integer> map = new ConcurrentSkipListMap<>();
map.put("A", 1);
map.put("C", 3);
map.put("B", 2);
// 獲取第一個元素
Map.Entry<String, Integer> firstEntry = map.firstEntry();
System.out.println("First entry: " + firstEntry);
// 獲取小於等於指定鍵的最大鍵值對
Map.Entry<String, Integer> floorEntry = map.floorEntry("B");
System.out.println("Floor entry of 'B': " + floorEntry);
// 獲取子映射
NavigableMap<String, Integer> subMap = map.subMap("A", true, "C", false);
System.out.println("SubMap from 'A' to 'C': " + subMap);
ConcurrentSkipListSet使用範例:
ConcurrentSkipListSet<Integer> set = new ConcurrentSkipListSet<>();
set.add(3);
set.add(1);
set.add(4);
set.add(2);
// 獲取第一個元素
Integer first = set.first();
System.out.println("First element: " + first);
// 獲取大於等於指定元素的最小元素
Integer ceiling = set.ceiling(3);
System.out.println("Ceiling of 3: " + ceiling);
// 獲取子集
NavigableSet<Integer> subSet = set.subSet(2, true, 4, false);
System.out.println("SubSet from 2 to 4: " + subSet);
使用場景:
優勢:
注意事項:
ConcurrentSkipListMap和ConcurrentSkipListSet為需要並行訪問的有序集合提供了高效的解決方案,使用時需要權衡有序性和並行性能的需求。
原子操作是指不可被中斷的一個或一系列操作,在並發程式設計中,原子操作可以確保在多執行緒環境下的資料一致性。
Java提供的原子類別位於java.util.concurrent.atomic包中,使用無鎖算法來實現原子操作。
主要的原子類別包括:
AtomicInteger和AtomicLong:
用於整數和長整數的原子操作。
範例:
AtomicInteger counter = new AtomicInteger(0);
int newValue = counter.incrementAndGet(); // 原子性地增加1並獲取新值
boolean success = counter.compareAndSet(newValue, 10); // 比較並設置新值
AtomicBoolean:
用於布林值的原子操作。
範例:
AtomicBoolean flag = new AtomicBoolean(false);
boolean oldValue = flag.getAndSet(true); // 設置新值並返回舊值
AtomicReference:
用於物件引用的原子操作。
範例:
AtomicReference<String> ref = new AtomicReference<>("初始值");
ref.set("新值");
String oldValue = ref.getAndUpdate(val -> val + "更新"); // 更新值並返回舊值
這些原子類別的主要特點:
使用場景:
優勢:
注意事項:
Java提供專門化的原子類別,包括原子陣列和進階的原子引用類別,這些類別擴展了原子操作的應用範圍,使得在更複雜的場景中也能保證資料的一致性。
原子陣列:
AtomicIntegerArray:
用於整數陣列的原子操作。
範例:
AtomicIntegerArray array = new AtomicIntegerArray(10);
array.set(0, 1);
int oldValue = array.getAndIncrement(0); // 原子性地增加指定索引的值
AtomicLongArray:
用於長整數陣列的原子操作。
AtomicReferenceArray:
用於物件引用陣列的原子操作。
進階原子引用類別:
AtomicStampedReference:
帶有版本戳記的原子引用,用於解決ABA問題。
範例:
AtomicStampedReference<String> ref = new AtomicStampedReference<>("初始值", 0);
int[] stampHolder = new int[1];
String value = ref.get(stampHolder);
boolean success = ref.compareAndSet(value, "新值", stampHolder[0], stampHolder[0] + 1);
AtomicMarkableReference:
帶有標記的原子引用,可用於實現邏輯刪除等操作。
範例:
AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("初始值", false);
boolean[] markHolder = new boolean[1];
String value = ref.get(markHolder);
boolean success = ref.compareAndSet(value, "新值", markHolder[0], true);
使用場景:
優勢:
注意事項:
選擇適當的並行集合:
合理使用原子類別:
避免過度同步:
注意伸縮性:
正確處理迭代:
合理使用批量操作:
注意記憶體使用:
正確處理異常:
效能測試和調優:
避免過度細化:
注意ABA問題:
正確使用執行緒池:
本篇文章同步刊載: JYI.TW
筆者個人的網站: JUNYI