Java ConcurrentModificationException 深度剖析開發(fā)調試日志的解決方案
前言
在Java多線程編程中,ConcurrentModificationException是一個常見的異常,它不僅出現(xiàn)在多線程環(huán)境,也會在單線程環(huán)境中出現(xiàn)。本文將深入分析這個異常的產生原因、觸發(fā)條件,并提供多種解決方案及其性能對比,幫助開發(fā)者在實際項目中做出最佳選擇。
異常概述
ConcurrentModificationException是Java集合框架中的一個運行時異常,它在以下情況下會被拋出:
- 當一個線程正在迭代集合,而另一個線程同時修改了該集合的結構(添加、刪除元素)
- 當在單線程環(huán)境中,使用迭代器遍歷集合的同時,通過集合自身的方法修改集合結構
這個異常是Java集合框架的一種**快速失敗(fail-fast)**機制,用于檢測并發(fā)修改,防止程序在不確定狀態(tài)下繼續(xù)執(zhí)行。
在我們的實際測試中,我們發(fā)現(xiàn)即使在單線程環(huán)境下,如果在遍歷過程中直接修改集合,也會拋出此異常。例如:
List<String> fruits = new ArrayList<>();
fruits.add("香蕉");
fruits.add("西瓜");
try {
for (String fruit : fruits) {
if (fruit.equals("香蕉")) {
fruits.remove(fruit); // 這里會拋出ConcurrentModificationException
}
}
} catch (ConcurrentModificationException e) {
System.out.println("異常信息: " + e.getMessage());
}單線程環(huán)境下的異常分析
異常復現(xiàn)
在單線程環(huán)境下,以下代碼會觸發(fā)ConcurrentModificationException:
List<String> list = new ArrayList<>();
list.add("item1");
list.add("item2");
list.add("item3");
// 使用for-each循環(huán)(底層使用Iterator)
for (String item : list) {
if ("item2".equals(item)) {
list.remove(item); // 這里會拋出ConcurrentModificationException
}
}源碼分析
為什么會拋出這個異常?讓我們看看ArrayList的Iterator實現(xiàn):
- 當創(chuàng)建Iterator時,會記錄當前集合的
modCount值(修改計數器)到expectedModCount - 每次調用
next()方法時,會檢查modCount是否等于expectedModCount - 如果不相等,說明集合在迭代過程中被修改,立即拋出
ConcurrentModificationException
關鍵源碼(簡化版):
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
public E next() {
checkForComodification();
// ...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}在我們的測試中,我們還發(fā)現(xiàn)不僅List集合會出現(xiàn)這個問題,Map集合同樣存在類似問題:
Map<String, String> caches = new HashMap<>();
caches.put("user@getAge@123@v1", "30");
caches.put("user@getAddress@456@v1", "New York");
String sameKeyPart = "user@get";
try {
Iterator<String> keys = caches.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
System.out.println("當前鍵: " + key);
if (key.startsWith(sameKeyPart)) {
caches.remove(key); // 這里會拋出ConcurrentModificationException
}
}
} catch (ConcurrentModificationException e) {
System.out.println("捕獲異常: " + e.getClass().getName());
}正確解決方法
- 使用Iterator的remove方法:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("item2".equals(item)) {
iterator.remove(); // 正確的方式
}
}在我們的測試代碼中,我們驗證了這種方法的有效性:
Map<String, String> caches = new HashMap<>();
caches.put("user@getName@123@v1", "John");
caches.put("user@getEmail@123@v1", "john@example.com");
String sameKeyPart = "user@get";
Iterator<String> keys = caches.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
if (key.startsWith(sameKeyPart)) {
keys.remove(); // 使用Iterator的remove方法
System.out.println("已刪除: " + key);
}
}- 使用Java 8+ 的removeIf方法:
list.removeIf(item -> "item2".equals(item));
在我們的測試中,這種方法同樣有效:
List<String> fruits = new ArrayList<>();
fruits.add("香蕉");
fruits.add("蘋果");
fruits.add("橙子");
fruits.removeIf(fruit -> fruit.equals("香蕉"));
System.out.println("刪除后: " + fruits);多線程環(huán)境下的異常分析
多線程環(huán)境下,即使使用了Iterator的remove方法,仍然可能發(fā)生ConcurrentModificationException,因為多個線程可能同時修改集合。
異常復現(xiàn)
List<String> list = new ArrayList<>();
// 初始化列表...
// 線程1:遍歷列表
new Thread(() -> {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
try {
Thread.sleep(100); // 模擬耗時操作
String item = iterator.next(); // 可能拋出異常
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
// 線程2:修改列表
new Thread(() -> {
try {
Thread.sleep(50);
list.add("newItem"); // 修改集合結構
} catch (Exception e) {
e.printStackTrace();
}
}).start();在我們的實際測試中,我們創(chuàng)建了一個更完整的示例:
private static void demoMultiThreadWithArrayList() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("Item " + i);
}
// 創(chuàng)建一個線程用于遍歷列表
Thread readerThread = new Thread(() -> {
try {
System.out.println("讀取線程開始遍歷");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
Thread.sleep(100); // 模擬處理時間
System.out.println("讀取線程: " + item);
}
System.out.println("讀取線程完成遍歷");
} catch (ConcurrentModificationException e) {
System.out.println("讀取線程捕獲異常: " + e.getClass().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 創(chuàng)建一個線程用于修改列表
Thread writerThread = new Thread(() -> {
try {
Thread.sleep(300); // 等待讀取線程開始
list.add("New Item"); // 添加新元素
System.out.println("修改線程添加了新元素");
Thread.sleep(100);
list.remove(0); // 刪除元素
System.out.println("修改線程刪除了元素");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
writerThread.start();
readerThread.start();
try {
writerThread.join();
readerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}線程安全分析
在多線程環(huán)境下,ArrayList等非線程安全集合存在以下問題:
- 結構性修改的原子性:添加或刪除元素不是原子操作
- 可見性問題:一個線程的修改對另一個線程不一定立即可見
- 一致性問題:迭代器可能看到集合的不一致狀態(tài)
解決方案對比
| 解決方案 | 適用場景 | 優(yōu)點 | 缺點 |
|---|---|---|---|
| Collections.synchronizedList | 讀寫頻率相近 | 簡單易用 | 性能較低,鎖粒度大 |
| CopyOnWriteArrayList | 讀多寫少 | 讀取無鎖,性能高 | 寫入性能差,內存占用高 |
| ConcurrentHashMap | 需要高并發(fā)Map | 分段鎖,性能好 | 僅適用于Map |
| CopiedIterator(自定義) | 讀寫分離場景 | 避免長時間鎖定 | 額外內存開銷 |
| 快照技術 | 一次性讀取后修改 | 簡單直觀 | 不適合大數據量 |
| Stream API | 函數式處理 | 代碼簡潔,可并行 | Java 8+才支持 |
在我們的測試中,我們對幾種主要的解決方案進行了實際驗證:
1. Collections.synchronizedList
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
// 需要注意的是,遍歷時仍需要手動同步
synchronized (synchronizedList) {
Iterator<String> iterator = synchronizedList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println("讀取線程: " + item);
}
}2. CopyOnWriteArrayList
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
// 可以安全地在遍歷過程中修改
for (String item : copyOnWriteList) {
System.out.println("當前元素: " + item);
copyOnWriteList.add("New Item"); // 不會拋出異常
}CopiedIterator實現(xiàn)與分析
CopiedIterator是一種自定義解決方案,它在創(chuàng)建迭代器時復制集合內容,從而避免并發(fā)修改異常。
實現(xiàn)代碼
public static class CopiedIterator<E> implements Iterator<E> {
private Iterator<E> iterator = null;
public CopiedIterator(Iterator<E> itr) {
LinkedList<E> list = new LinkedList<>();
while(itr.hasNext()) {
list.add(itr.next());
}
this.iterator = list.iterator();
}
public boolean hasNext() {
return this.iterator.hasNext();
}
public void remove() {
throw new UnsupportedOperationException("這是一個只讀迭代器");
}
public E next() {
return this.iterator.next();
}
}使用方式
List<String> list = new ArrayList<>();
// 初始化列表...
// 創(chuàng)建CopiedIterator
Iterator<String> safeIterator;
synchronized(list) {
safeIterator = new CopiedIterator<>(list.iterator());
}
// 安全遍歷,不會拋出ConcurrentModificationException
while(safeIterator.hasNext()) {
String item = safeIterator.next();
// 處理元素...
}在我們的實際測試中,我們發(fā)現(xiàn)這種方案在特定場景下非常有效:
public static void perform() {
Iterator<String> iterator;
synchronized(list) {
iterator = new CopiedIterator<>(list.iterator());
}
System.out.println("獲取到只讀迭代器,開始遍歷");
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println("遍歷元素: " + item);
try {
Thread.sleep(100); // 模擬處理時間
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("遍歷完成");
}優(yōu)缺點分析
優(yōu)點:
- 避免了長時間鎖定集合
- 適用于任何實現(xiàn)了Iterator接口的集合
- 實現(xiàn)簡單,容易理解
缺點:
- 額外的內存開銷,尤其是對大型集合
- 只能提供集合的快照,無法反映后續(xù)修改
- 不支持修改操作(如remove)
在我們的性能測試中,我們發(fā)現(xiàn)對于包含10000個元素的列表,CopiedIterator的額外開銷大約為10-15毫秒,這對于需要長時間處理的場景來說是可以接受的。
高級解決方案
1. ConcurrentHashMap
ConcurrentHashMap是一個高性能的線程安全Map實現(xiàn),它使用分段鎖技術提高并發(fā)性能。
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
// 可以安全地在遍歷過程中修改
for (String key : concurrentMap.keySet()) {
concurrentMap.put("newKey", "newValue"); // 不會拋出異常
}在我們的測試中,我們驗證了ConcurrentHashMap的線程安全性:
private static void demoConcurrentHashMap() {
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key1", "value1");
concurrentMap.put("key2", "value2");
// 測試ConcurrentHashMap
for (String key : concurrentMap.keySet()) {
if (key.equals("key2")) {
concurrentMap.put("key4", "value4"); // 不會拋出異常
System.out.println("添加了新鍵值對: key4=value4");
}
}
System.out.println("ConcurrentHashMap最終大小: " + concurrentMap.size());
}2. CopyOnWriteArrayList/Set
CopyOnWriteArrayList和CopyOnWriteArraySet在每次寫操作時都會復制整個底層數組,非常適合讀多寫少的場景。
List<String> cowList = new CopyOnWriteArrayList<>();
// 可以安全地在遍歷過程中修改
for (String item : cowList) {
cowList.add("newItem"); // 不會拋出異常
}我們的測試代碼驗證了這一點:
private static void demoCopyOnWriteArraySet() {
// 創(chuàng)建CopyOnWriteArraySet
Set<String> cowSet = new CopyOnWriteArraySet<>();
cowSet.add("item1");
cowSet.add("item2");
cowSet.add("item3");
System.out.println("\n嘗試在遍歷CopyOnWriteArraySet時修改:");
for (String item : cowSet) {
System.out.println("當前元素: " + item);
cowSet.add("item4"); // 不會拋出異常
}
System.out.println("CopyOnWriteArraySet內容: " + cowSet);
}3. 快照技術
快照技術是一種簡單的解決方案,適用于一次性讀取后修改的場景。
List<String> originalList = new ArrayList<>();
// 初始化列表...
// 創(chuàng)建快照
List<String> snapshot = new ArrayList<>(originalList);
// 遍歷快照,修改原始列表
for (String item : snapshot) {
if (someCondition(item)) {
originalList.remove(item);
}
}我們在測試中也驗證了這種技術:
private static void demoSnapshotTechnique() {
List<String> originalList = new ArrayList<>();
originalList.add("item1");
originalList.add("item2");
originalList.add("item3");
System.out.println("原始列表: " + originalList);
List<String> snapshot = new ArrayList<>(originalList);
System.out.println("遍歷快照并修改原始列表:");
for (String item : snapshot) {
System.out.println("當前元素: " + item);
if (item.equals("item2")) {
originalList.remove(item);
}
}
System.out.println("修改后原始列表: " + originalList);
System.out.println("快照內容保持不變: " + snapshot);
}4. Stream API
Java 8引入的Stream API提供了一種函數式處理集合的方式,可以避免顯式迭代。
List<String> result = list.stream()
.filter(item -> !item.equals("item2"))
.collect(Collectors.toList());
在我們的測試中,我們使用了Stream API的各種功能:
private static void demoStreamAPI() {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("grape");
System.out.println("\n使用Stream API過濾元素:");
List<String> filteredList = list.stream()
.filter(item -> !item.equals("banana"))
.collect(Collectors.toList());
System.out.println("過濾后: " + filteredList);
System.out.println("\n使用Stream API轉換元素:");
List<String> upperCaseList = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("轉換后: " + upperCaseList);
}性能測試與對比
我們對不同解決方案進行了性能測試,以下是結果分析:
1. 遍歷性能對比(10,000元素)
| 解決方案 | 平均耗時(ms) |
|---|---|
| 普通Iterator | 1-2 |
| CopiedIterator | 10-15 |
| CopyOnWriteArrayList | 1-2 |
| Collections.synchronizedList | 3-5 |
| Stream API (順序) | 5-8 |
| Stream API (并行) | 2-4 |
在我們的性能測試代碼中,我們進行了實際測量:
private static void performanceTest() {
// 準備大數據集
List<String> largeList = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
largeList.add("Item-" + i);
}
// 測試普通Iterator
long startTime = System.nanoTime();
Iterator<String> normalIterator = largeList.iterator();
int count = 0;
while (normalIterator.hasNext()) {
normalIterator.next();
count++;
}
long normalTime = System.nanoTime() - startTime;
// 測試CopiedIterator
startTime = System.nanoTime();
Iterator<String> copiedIterator = new CopiedIterator<>(largeList.iterator());
count = 0;
while (copiedIterator.hasNext()) {
copiedIterator.next();
count++;
}
long copiedTime = System.nanoTime() - startTime;
System.out.println("普通Iterator遍歷時間: " + TimeUnit.NANOSECONDS.toMillis(normalTime) + " 毫秒");
System.out.println("CopiedIterator遍歷時間: " + TimeUnit.NANOSECONDS.toMillis(copiedTime) + " 毫秒");
System.out.println("CopiedIterator額外開銷: " + (copiedTime - normalTime) / 1000000.0 + " 毫秒");
}2. 修改性能對比(10,000元素,添加操作)
| 解決方案 | 平均耗時(ms) |
|---|---|
| ArrayList | 0.1-0.2 |
| CopyOnWriteArrayList | 50-100 |
| Collections.synchronizedList | 0.5-1 |
| ConcurrentHashMap (put) | 0.2-0.5 |
3. 內存占用對比
| 解決方案 | 相對內存占用 |
|---|---|
| ArrayList | 1x |
| CopiedIterator | 2x |
| CopyOnWriteArrayList (寫操作時) | 2x |
| 快照技術 | 2x |
異常處理機制深入分析
fail-fast機制原理
Java集合框架中的fail-fast機制是一種錯誤檢測機制,它能幫助開發(fā)者盡早發(fā)現(xiàn)程序中的并發(fā)修改問題。當多個線程對集合進行結構上的改變時,就可能產生fail-fast事件。
在ArrayList中,modCount變量記錄了集合結構修改的次數。每次調用add、remove等修改結構的方法時,modCount都會增加。同時,Iterator在創(chuàng)建時會保存當前的modCount值作為expectedModCount。每次調用Iterator的next()方法時,都會檢查modCount是否與expectedModCount相等,如果不相等則拋出ConcurrentModificationException。
// ArrayList中的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// AbstractList中的ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
modCount++; // 修改計數器增加
ensureExplicitCapacity(minCapacity);
}異常傳播與處理
在實際應用中,我們需要合理處理ConcurrentModificationException異常。以下是我們推薦的處理方式:
- 預防為主:使用線程安全的集合類或同步機制來避免異常的發(fā)生
- 捕獲并記錄:在無法避免異常的情況下,捕獲異常并記錄日志
- 優(yōu)雅降級:提供備選方案,確保系統(tǒng)在異常情況下仍能正常運行
public class SafeListProcessor {
private List<String> dataList;
public SafeListProcessor(List<String> dataList) {
this.dataList = dataList;
}
public void processList() {
Iterator<String> iterator = null;
synchronized(dataList) {
iterator = new CopiedIterator<>(dataList.iterator());
}
try {
while (iterator.hasNext()) {
String item = iterator.next();
// 處理元素
processItem(item);
}
} catch (ConcurrentModificationException e) {
// 記錄異常日志
System.err.println("檢測到并發(fā)修改異常: " + e.getMessage());
// 可以選擇重試或使用備選方案
handleConcurrentModification();
}
}
private void processItem(String item) {
// 處理單個元素
System.out.println("處理元素: " + item);
}
private void handleConcurrentModification() {
// 處理并發(fā)修改異常的備選方案
System.out.println("使用備選方案處理數據");
}
}實際應用建議
選擇合適的解決方案
在實際項目中,我們需要根據具體場景選擇合適的解決方案:
- 讀多寫少場景:
- 推薦使用CopyOnWriteArrayList/CopyOnWriteArraySet
- 適用于緩存、配置信息等場景
- 高并發(fā)讀寫場景:
- 推薦使用ConcurrentHashMap
- 適用于需要高并發(fā)訪問的Map結構
- 需要長時間遍歷的場景:
- 推薦使用CopiedIterator或快照技術
- 適用于需要對大量數據進行復雜處理的場景
- 簡單過濾或轉換場景:
- 推薦使用Stream API
- 代碼簡潔,可讀性強
代碼示例
以下是我們項目中實際使用的代碼示例:
// 使用CopyOnWriteArrayList處理配置信息
public class ConfigManager {
private CopyOnWriteArrayList<ConfigItem> configItems = new CopyOnWriteArrayList<>();
public void addConfig(ConfigItem item) {
configItems.add(item);
}
public List<ConfigItem> getActiveConfigs() {
// 可以安全地遍歷,即使其他線程正在修改
return configItems.stream()
.filter(ConfigItem::isActive)
.collect(Collectors.toList());
}
}
// 使用ConcurrentHashMap處理用戶會話
public class SessionManager {
private ConcurrentHashMap<String, UserSession> sessions = new ConcurrentHashMap<>();
public void addSession(String sessionId, UserSession session) {
sessions.put(sessionId, session);
}
public void cleanupExpiredSessions() {
// 可以安全地遍歷并修改
sessions.entrySet().removeIf(entry -> entry.getValue().isExpired());
}
}
// 使用CopiedIterator處理長時間運行的任務
public class DataProcessor {
private List<DataItem> dataItems;
public void processLargeDataSet() {
Iterator<DataItem> iterator;
synchronized(dataItems) {
iterator = new CopiedIterator<>(dataItems.iterator());
}
// 長時間處理不會阻塞其他線程對dataItems的修改
while (iterator.hasNext()) {
DataItem item = iterator.next();
processComplexCalculation(item);
}
}
}性能優(yōu)化建議
- 合理預估集合大小:
- 使用帶初始容量的構造函數避免頻繁擴容
- 例如:
new ArrayList<>(1000)而不是new ArrayList<>();
- 選擇合適的數據結構:
- 頻繁隨機訪問:ArrayList
- 頻繁插入刪除:LinkedList
- 需要排序:TreeSet/TreeMap
- 唯一性要求:HashSet/HashMap
- 減少鎖競爭:
- 縮小同步塊范圍
- 使用讀寫鎖分離讀寫操作
- 考慮使用無鎖數據結構
最佳實踐總結
單線程環(huán)境
- 避免在for-each循環(huán)中修改集合
- 使用Iterator的remove()方法
- 使用Java 8+的removeIf()、replaceAll()等方法
- 創(chuàng)建集合副本進行遍歷
- 批量操作優(yōu)于單個操作
- 使用addAll()、removeAll()等批量方法
- 使用Stream API進行批量處理
在我們的測試中,我們發(fā)現(xiàn)removeIf()方法特別適用于簡單的過濾操作:
List<String> fruits = new ArrayList<>();
fruits.add("香蕉");
fruits.add("蘋果");
fruits.add("橙子");
// 使用removeIf進行過濾
fruits.removeIf(fruit -> fruit.equals("香蕉"));
System.out.println("刪除后: " + fruits);多線程環(huán)境
- 選擇合適的線程安全集合
- 讀多寫少:CopyOnWriteArrayList/Set
- 讀寫頻率相近:Collections.synchronizedList + 同步塊
- 高并發(fā)Map:ConcurrentHashMap
- 避免長時間鎖定集合
- 使用CopiedIterator或快照技術
- 縮小同步塊范圍
- 考慮使用并發(fā)工具類
- BlockingQueue系列
- ConcurrentSkipListMap/Set
在我們的多線程測試中,我們發(fā)現(xiàn)CopyOnWriteArrayList在讀多寫少的場景下表現(xiàn)優(yōu)異:
private static void demoCopyOnWriteArrayList() {
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
copyOnWriteList.add("Item " + i);
}
Thread readerThread = new Thread(() -> {
System.out.println("讀取線程開始遍歷CopyOnWriteArrayList");
Iterator<String> iterator = copyOnWriteList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println("讀取線程: " + item);
try {
Thread.sleep(100); // 模擬處理時間
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("讀取線程完成遍歷");
});
Thread writerThread = new Thread(() -> {
try {
System.out.println("修改線程開始修改CopyOnWriteArrayList");
copyOnWriteList.add("New Item"); // 添加新元素
System.out.println("修改線程添加了新元素");
Thread.sleep(100);
copyOnWriteList.remove(0); // 刪除元素
System.out.println("修改線程刪除了元素");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
readerThread.start();
writerThread.start();
try {
readerThread.join();
writerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("最終列表大小: " + copyOnWriteList.size());
}性能優(yōu)化
- 根據訪問模式選擇集合
- 隨機訪問多:ArrayList
- 插入刪除多:LinkedList
- 唯一性要求:HashSet/TreeSet
- 預估集合大小
- 使用構造函數指定初始容量
- 避免頻繁擴容
- 減少不必要的復制
- 謹慎使用CopyOnWrite集合
- 優(yōu)化CopiedIterator實現(xiàn)
在我們的測試中,我們發(fā)現(xiàn)對于大數據集,Stream API的并行處理能力非常強大:
private static void demoParallelStream() {
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
numbers.add(i);
}
System.out.println("\n使用并行流處理大量數據:");
long startTime = System.nanoTime();
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
long sequentialTime = System.nanoTime() - startTime;
startTime = System.nanoTime();
int parallelSum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
long parallelTime = System.nanoTime() - startTime;
System.out.println("順序流處理時間: " + TimeUnit.NANOSECONDS.toMicros(sequentialTime) + " 微秒");
System.out.println("并行流處理時間: " + TimeUnit.NANOSECONDS.toMicros(parallelTime) + " 微秒");
System.out.println("結果驗證: " + (sum == parallelSum ? "正確" : "錯誤"));
}到此這篇關于Java ConcurrentModificationException 深度剖析開發(fā)調試日志的解決方案的文章就介紹到這了,更多相關Java ConcurrentModificationException調試日志內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Java?報錯?java.util.ConcurrentModificationException:?null?的原因及解決方案
- Java ConcurrentModificationException異常解決案例詳解
- 詳解Java刪除Map中元素java.util.ConcurrentModificationException”異常解決
- Java源碼解析ArrayList及ConcurrentModificationException
- 出現(xiàn)java.util.ConcurrentModificationException 問題及解決辦法
- java.util.ConcurrentModificationException 解決方法
- java 集合并發(fā)操作出現(xiàn)的異常ConcurrentModificationException
相關文章
在IDEA中配置Selenium和WebDriver的具體操作
在自動化測試領域Selenium是一款非常流行的開源工具,它支持多種瀏覽器,并提供了豐富的API供開發(fā)者使用,而WebDriver則是Selenium的一個重要組件,它負責驅動瀏覽器執(zhí)行測試腳本,這篇文章主要給大家介紹了在IDEA中配置Selenium和WebDriver的具體操作,需要的朋友可以參考下2024-10-10
SpringBoot生成PDF的五種實現(xiàn)方法總結
這篇文章主要介紹了SpringBoot生成PDF的五種實現(xiàn)方法,在開發(fā)中經常會遇到需要進行對一些數據進行動態(tài)導出PDF文件,然后讓用戶自己選擇是否需要打印出來,這篇文章我們來介紹五種實現(xiàn)方法,需要的朋友可以參考下2024-10-10
javax.mail.SendFailedException: Sending failed問題原因
這篇文章主要介紹了javax.mail.SendFailedException: Sending failed問題原因,需要的朋友可以參考下2015-05-05

