最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java ConcurrentModificationException 深度剖析開發(fā)調(diào)試日志的解決方案

 更新時(shí)間:2025年09月05日 09:24:01   作者:熊貓釣魚>_>  
在Java多線程編程中,ConcurrentModificationException是一個(gè)常見的異常,它不僅出現(xiàn)在多線程環(huán)境,也會在單線程環(huán)境中出現(xiàn),本文深入分析這個(gè)異常的產(chǎn)生原因、觸發(fā)條件,并提供多種解決方案及其性能對比,幫助開發(fā)者在實(shí)際項(xiàng)目中做出最佳選擇,感興趣的朋友跟隨小編一起看看吧

前言

在Java多線程編程中,ConcurrentModificationException是一個(gè)常見的異常,它不僅出現(xiàn)在多線程環(huán)境,也會在單線程環(huán)境中出現(xiàn)。本文將深入分析這個(gè)異常的產(chǎn)生原因、觸發(fā)條件,并提供多種解決方案及其性能對比,幫助開發(fā)者在實(shí)際項(xiàng)目中做出最佳選擇。

異常概述

ConcurrentModificationException是Java集合框架中的一個(gè)運(yùn)行時(shí)異常,它在以下情況下會被拋出:

  • 當(dāng)一個(gè)線程正在迭代集合,而另一個(gè)線程同時(shí)修改了該集合的結(jié)構(gòu)(添加、刪除元素)
  • 當(dāng)在單線程環(huán)境中,使用迭代器遍歷集合的同時(shí),通過集合自身的方法修改集合結(jié)構(gòu)

這個(gè)異常是Java集合框架的一種**快速失敗(fail-fast)**機(jī)制,用于檢測并發(fā)修改,防止程序在不確定狀態(tài)下繼續(xù)執(zhí)行。

在我們的實(shí)際測試中,我們發(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)境下的異常分析

異常復(fù)現(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
    }
}

源碼分析

為什么會拋出這個(gè)異常?讓我們看看ArrayList的Iterator實(shí)現(xiàn):

  1. 當(dāng)創(chuàng)建Iterator時(shí),會記錄當(dāng)前集合的modCount值(修改計(jì)數(shù)器)到expectedModCount
  2. 每次調(diào)用next()方法時(shí),會檢查modCount是否等于expectedModCount
  3. 如果不相等,說明集合在迭代過程中被修改,立即拋出ConcurrentModificationException

關(guān)鍵源碼(簡化版):

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)這個(gè)問題,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("當(dāng)前鍵: " + key);
        if (key.startsWith(sameKeyPart)) {
            caches.remove(key); // 這里會拋出ConcurrentModificationException
        }
    }
} catch (ConcurrentModificationException e) {
    System.out.println("捕獲異常: " + e.getClass().getName());
}

正確解決方法

  1. 使用Iterator的remove方法
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if ("item2".equals(item)) {
        iterator.remove(); // 正確的方式
    }
}

在我們的測試代碼中,我們驗(yàn)證了這種方法的有效性:

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);
    }
}
  1. 使用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,因?yàn)槎鄠€(gè)線程可能同時(shí)修改集合。

異常復(fù)現(xiàn)

List<String> list = new ArrayList<>();
// 初始化列表...
// 線程1:遍歷列表
new Thread(() -> {
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        try {
            Thread.sleep(100); // 模擬耗時(shí)操作
            String item = iterator.next(); // 可能拋出異常
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();
// 線程2:修改列表
new Thread(() -> {
    try {
        Thread.sleep(50);
        list.add("newItem"); // 修改集合結(jié)構(gòu)
    } catch (Exception e) {
        e.printStackTrace();
    }
}).start();

在我們的實(shí)際測試中,我們創(chuàng)建了一個(gè)更完整的示例:

private static void demoMultiThreadWithArrayList() {
    List<String> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add("Item " + i);
    }
    // 創(chuàng)建一個(gè)線程用于遍歷列表
    Thread readerThread = new Thread(() -> {
        try {
            System.out.println("讀取線程開始遍歷");
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String item = iterator.next();
                Thread.sleep(100); // 模擬處理時(shí)間
                System.out.println("讀取線程: " + item);
            }
            System.out.println("讀取線程完成遍歷");
        } catch (ConcurrentModificationException e) {
            System.out.println("讀取線程捕獲異常: " + e.getClass().getName());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
    // 創(chuàng)建一個(gè)線程用于修改列表
    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等非線程安全集合存在以下問題:

  1. 結(jié)構(gòu)性修改的原子性:添加或刪除元素不是原子操作
  2. 可見性問題:一個(gè)線程的修改對另一個(gè)線程不一定立即可見
  3. 一致性問題:迭代器可能看到集合的不一致狀態(tài)

解決方案對比

解決方案適用場景優(yōu)點(diǎn)缺點(diǎn)
Collections.synchronizedList讀寫頻率相近簡單易用性能較低,鎖粒度大
CopyOnWriteArrayList讀多寫少讀取無鎖,性能高寫入性能差,內(nèi)存占用高
ConcurrentHashMap需要高并發(fā)Map分段鎖,性能好僅適用于Map
CopiedIterator(自定義)讀寫分離場景避免長時(shí)間鎖定額外內(nèi)存開銷
快照技術(shù)一次性讀取后修改簡單直觀不適合大數(shù)據(jù)量
Stream API函數(shù)式處理代碼簡潔,可并行Java 8+才支持

在我們的測試中,我們對幾種主要的解決方案進(jìn)行了實(shí)際驗(yàn)證:

1. Collections.synchronizedList

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
// 需要注意的是,遍歷時(shí)仍需要手動(dòng)同步
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("當(dāng)前元素: " + item);
    copyOnWriteList.add("New Item"); // 不會拋出異常
}

CopiedIterator實(shí)現(xiàn)與分析

CopiedIterator是一種自定義解決方案,它在創(chuàng)建迭代器時(shí)復(fù)制集合內(nèi)容,從而避免并發(fā)修改異常。

實(shí)現(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("這是一個(gè)只讀迭代器");
    }
    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();
    // 處理元素...
}

在我們的實(shí)際測試中,我們發(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); // 模擬處理時(shí)間
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    System.out.println("遍歷完成");
}

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)

  • 避免了長時(shí)間鎖定集合
  • 適用于任何實(shí)現(xiàn)了Iterator接口的集合
  • 實(shí)現(xiàn)簡單,容易理解

缺點(diǎn)

  • 額外的內(nèi)存開銷,尤其是對大型集合
  • 只能提供集合的快照,無法反映后續(xù)修改
  • 不支持修改操作(如remove)

在我們的性能測試中,我們發(fā)現(xiàn)對于包含10000個(gè)元素的列表,CopiedIterator的額外開銷大約為10-15毫秒,這對于需要長時(shí)間處理的場景來說是可以接受的。

高級解決方案

1. ConcurrentHashMap

ConcurrentHashMap是一個(gè)高性能的線程安全Map實(shí)現(xiàn),它使用分段鎖技術(shù)提高并發(fā)性能。

Map<String, String> concurrentMap = new ConcurrentHashMap<>();
// 可以安全地在遍歷過程中修改
for (String key : concurrentMap.keySet()) {
    concurrentMap.put("newKey", "newValue"); // 不會拋出異常
}

在我們的測試中,我們驗(yàn)證了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

CopyOnWriteArrayListCopyOnWriteArraySet在每次寫操作時(shí)都會復(fù)制整個(gè)底層數(shù)組,非常適合讀多寫少的場景。

List<String> cowList = new CopyOnWriteArrayList<>();
// 可以安全地在遍歷過程中修改
for (String item : cowList) {
    cowList.add("newItem"); // 不會拋出異常
}

我們的測試代碼驗(yàn)證了這一點(diǎn):

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時(shí)修改:");
    for (String item : cowSet) {
        System.out.println("當(dāng)前元素: " + item);
        cowSet.add("item4"); // 不會拋出異常
    }
    System.out.println("CopyOnWriteArraySet內(nèi)容: " + cowSet);
}

3. 快照技術(shù)

快照技術(shù)是一種簡單的解決方案,適用于一次性讀取后修改的場景。

List<String> originalList = new ArrayList<>();
// 初始化列表...
// 創(chuàng)建快照
List<String> snapshot = new ArrayList<>(originalList);
// 遍歷快照,修改原始列表
for (String item : snapshot) {
    if (someCondition(item)) {
        originalList.remove(item);
    }
}

我們在測試中也驗(yàn)證了這種技術(shù):

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("當(dāng)前元素: " + item);
        if (item.equals("item2")) {
            originalList.remove(item);
        }
    }
    System.out.println("修改后原始列表: " + originalList);
    System.out.println("快照內(nèi)容保持不變: " + snapshot);
}

4. Stream API

Java 8引入的Stream API提供了一種函數(shù)式處理集合的方式,可以避免顯式迭代。

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轉(zhuǎn)換元素:");
    List<String> upperCaseList = list.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());
    System.out.println("轉(zhuǎn)換后: " + upperCaseList);
}

性能測試與對比

我們對不同解決方案進(jìn)行了性能測試,以下是結(jié)果分析:

1. 遍歷性能對比(10,000元素)

解決方案平均耗時(shí)(ms)
普通Iterator1-2
CopiedIterator10-15
CopyOnWriteArrayList1-2
Collections.synchronizedList3-5
Stream API (順序)5-8
Stream API (并行)2-4

在我們的性能測試代碼中,我們進(jìn)行了實(shí)際測量:

private static void performanceTest() {
    // 準(zhǔn)備大數(shù)據(jù)集
    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遍歷時(shí)間: " + TimeUnit.NANOSECONDS.toMillis(normalTime) + " 毫秒");
    System.out.println("CopiedIterator遍歷時(shí)間: " + TimeUnit.NANOSECONDS.toMillis(copiedTime) + " 毫秒");
    System.out.println("CopiedIterator額外開銷: " + (copiedTime - normalTime) / 1000000.0 + " 毫秒");
}

2. 修改性能對比(10,000元素,添加操作)

解決方案平均耗時(shí)(ms)
ArrayList0.1-0.2
CopyOnWriteArrayList50-100
Collections.synchronizedList0.5-1
ConcurrentHashMap (put)0.2-0.5

3. 內(nèi)存占用對比

解決方案相對內(nèi)存占用
ArrayList1x
CopiedIterator2x
CopyOnWriteArrayList (寫操作時(shí))2x
快照技術(shù)2x

異常處理機(jī)制深入分析

fail-fast機(jī)制原理

Java集合框架中的fail-fast機(jī)制是一種錯(cuò)誤檢測機(jī)制,它能幫助開發(fā)者盡早發(fā)現(xiàn)程序中的并發(fā)修改問題。當(dāng)多個(gè)線程對集合進(jìn)行結(jié)構(gòu)上的改變時(shí),就可能產(chǎn)生fail-fast事件。

在ArrayList中,modCount變量記錄了集合結(jié)構(gòu)修改的次數(shù)。每次調(diào)用add、remove等修改結(jié)構(gòu)的方法時(shí),modCount都會增加。同時(shí),Iterator在創(chuàng)建時(shí)會保存當(dāng)前的modCount值作為expectedModCount。每次調(diào)用Iterator的next()方法時(shí),都會檢查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++; // 修改計(jì)數(shù)器增加
    ensureExplicitCapacity(minCapacity);
}

異常傳播與處理

在實(shí)際應(yīng)用中,我們需要合理處理ConcurrentModificationException異常。以下是我們推薦的處理方式:

  1. 預(yù)防為主:使用線程安全的集合類或同步機(jī)制來避免異常的發(fā)生
  2. 捕獲并記錄:在無法避免異常的情況下,捕獲異常并記錄日志
  3. 優(yōu)雅降級:提供備選方案,確保系統(tǒng)在異常情況下仍能正常運(yùn)行
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) {
        // 處理單個(gè)元素
        System.out.println("處理元素: " + item);
    }
    private void handleConcurrentModification() {
        // 處理并發(fā)修改異常的備選方案
        System.out.println("使用備選方案處理數(shù)據(jù)");
    }
}

實(shí)際應(yīng)用建議

選擇合適的解決方案

在實(shí)際項(xiàng)目中,我們需要根據(jù)具體場景選擇合適的解決方案:

  1. 讀多寫少場景
    • 推薦使用CopyOnWriteArrayList/CopyOnWriteArraySet
    • 適用于緩存、配置信息等場景
  2. 高并發(fā)讀寫場景
    • 推薦使用ConcurrentHashMap
    • 適用于需要高并發(fā)訪問的Map結(jié)構(gòu)
  3. 需要長時(shí)間遍歷的場景
    • 推薦使用CopiedIterator或快照技術(shù)
    • 適用于需要對大量數(shù)據(jù)進(jìn)行復(fù)雜處理的場景
  4. 簡單過濾或轉(zhuǎn)換場景
    • 推薦使用Stream API
    • 代碼簡潔,可讀性強(qiáng)

代碼示例

以下是我們項(xiàng)目中實(shí)際使用的代碼示例:

// 使用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處理長時(shí)間運(yùn)行的任務(wù)
public class DataProcessor {
    private List<DataItem> dataItems;
    public void processLargeDataSet() {
        Iterator<DataItem> iterator;
        synchronized(dataItems) {
            iterator = new CopiedIterator<>(dataItems.iterator());
        }
        // 長時(shí)間處理不會阻塞其他線程對dataItems的修改
        while (iterator.hasNext()) {
            DataItem item = iterator.next();
            processComplexCalculation(item);
        }
    }
}

性能優(yōu)化建議

  1. 合理預(yù)估集合大小
    • 使用帶初始容量的構(gòu)造函數(shù)避免頻繁擴(kuò)容
    • 例如:new ArrayList<>(1000)而不是new ArrayList<>();
  2. 選擇合適的數(shù)據(jù)結(jié)構(gòu)
    • 頻繁隨機(jī)訪問:ArrayList
    • 頻繁插入刪除:LinkedList
    • 需要排序:TreeSet/TreeMap
    • 唯一性要求:HashSet/HashMap
  3. 減少鎖競爭
    • 縮小同步塊范圍
    • 使用讀寫鎖分離讀寫操作
    • 考慮使用無鎖數(shù)據(jù)結(jié)構(gòu)

最佳實(shí)踐總結(jié)

單線程環(huán)境

  1. 避免在for-each循環(huán)中修改集合
    • 使用Iterator的remove()方法
    • 使用Java 8+的removeIf()、replaceAll()等方法
    • 創(chuàng)建集合副本進(jìn)行遍歷
  2. 批量操作優(yōu)于單個(gè)操作
    • 使用addAll()、removeAll()等批量方法
    • 使用Stream API進(jìn)行批量處理

在我們的測試中,我們發(fā)現(xiàn)removeIf()方法特別適用于簡單的過濾操作:

List<String> fruits = new ArrayList<>();
fruits.add("香蕉");
fruits.add("蘋果");
fruits.add("橙子");
// 使用removeIf進(jìn)行過濾
fruits.removeIf(fruit -> fruit.equals("香蕉"));
System.out.println("刪除后: " + fruits);

多線程環(huán)境

  1. 選擇合適的線程安全集合
    • 讀多寫少:CopyOnWriteArrayList/Set
    • 讀寫頻率相近:Collections.synchronizedList + 同步塊
    • 高并發(fā)Map:ConcurrentHashMap
  2. 避免長時(shí)間鎖定集合
    • 使用CopiedIterator或快照技術(shù)
    • 縮小同步塊范圍
  3. 考慮使用并發(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); // 模擬處理時(shí)間
            } 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)化

  1. 根據(jù)訪問模式選擇集合
    • 隨機(jī)訪問多:ArrayList
    • 插入刪除多:LinkedList
    • 唯一性要求:HashSet/TreeSet
  2. 預(yù)估集合大小
    • 使用構(gòu)造函數(shù)指定初始容量
    • 避免頻繁擴(kuò)容
  3. 減少不必要的復(fù)制
    • 謹(jǐn)慎使用CopyOnWrite集合
    • 優(yōu)化CopiedIterator實(shí)現(xiàn)

在我們的測試中,我們發(fā)現(xiàn)對于大數(shù)據(jù)集,Stream API的并行處理能力非常強(qiáng)大:

private static void demoParallelStream() {
    List<Integer> numbers = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        numbers.add(i);
    }
    System.out.println("\n使用并行流處理大量數(shù)據(jù):");
    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("順序流處理時(shí)間: " + TimeUnit.NANOSECONDS.toMicros(sequentialTime) + " 微秒");
    System.out.println("并行流處理時(shí)間: " + TimeUnit.NANOSECONDS.toMicros(parallelTime) + " 微秒");
    System.out.println("結(jié)果驗(yàn)證: " + (sum == parallelSum ? "正確" : "錯(cuò)誤"));
}

到此這篇關(guān)于Java ConcurrentModificationException 深度剖析開發(fā)調(diào)試日志的解決方案的文章就介紹到這了,更多相關(guān)Java ConcurrentModificationException調(diào)試日志內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java圖片驗(yàn)證碼生成教程詳解

    java圖片驗(yàn)證碼生成教程詳解

    這篇文章主要為大家詳細(xì)介紹了java圖片驗(yàn)證碼生成教程,從簡單到復(fù)雜,從本地到前后臺,感興趣的小伙伴們可以參考一下
    2016-07-07
  • MyBatis使用CASE WHEN進(jìn)行批量更新的高效寫法

    MyBatis使用CASE WHEN進(jìn)行批量更新的高效寫法

    當(dāng)我們使用mybatis的時(shí)候,可能經(jīng)常會碰到一批數(shù)據(jù)的批量更新問題,因?yàn)槿绻粭l數(shù)據(jù)一更新,那每一條數(shù)據(jù)就需要涉及到一次數(shù)據(jù)庫的操作,包括網(wǎng)絡(luò)IO以及磁盤IO,可想而知,這個(gè)效率是非常低下的,那么今天我們就來總結(jié)一下,如何使用mybatis做批量更新,需要的朋友可以參考下
    2025-10-10
  • Springboot多數(shù)據(jù)源配置之整合dynamic-datasource方式

    Springboot多數(shù)據(jù)源配置之整合dynamic-datasource方式

    這篇文章主要介紹了Springboot多數(shù)據(jù)源配置之整合dynamic-datasource方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 淺談Java中的n種隨機(jī)數(shù)產(chǎn)生辦法

    淺談Java中的n種隨機(jī)數(shù)產(chǎn)生辦法

    眾所周知,隨機(jī)數(shù)是任何一種編程語言最基本的特征之一。而生成隨機(jī)數(shù)的基本方式也是相同的:產(chǎn)生一個(gè)0到1之間的隨機(jī)數(shù)??此坪唵?,但有時(shí)我們也會忽略了一些有趣的功能。
    2015-09-09
  • Mybatis查詢數(shù)據(jù)的項(xiàng)目實(shí)現(xiàn)

    Mybatis查詢數(shù)據(jù)的項(xiàng)目實(shí)現(xiàn)

    MyBatis通過XML配置文件或注解,把Java對象映射到對應(yīng)的數(shù)據(jù)庫表中,實(shí)現(xiàn)對象關(guān)系和數(shù)據(jù)關(guān)系的互相轉(zhuǎn)換,從而使得Java應(yīng)用程序能夠更簡單的操作和讀取數(shù)據(jù)庫,本文就詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下
    2023-09-09
  • Java生成獨(dú)一無二的工單號實(shí)例

    Java生成獨(dú)一無二的工單號實(shí)例

    這篇文章主要介紹了Java生成獨(dú)一無二的工單號實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • 詳解Spring?@Lazy注解為什么能破解死循環(huán)

    詳解Spring?@Lazy注解為什么能破解死循環(huán)

    這篇文章主要來和大家探討一下Spring中的@Lazy注解為什么能破解死循環(huán),文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下
    2023-07-07
  • 使用Spring底層組件實(shí)現(xiàn)Aware接口

    使用Spring底層組件實(shí)現(xiàn)Aware接口

    這篇文章主要介紹了使用Spring底層組件實(shí)現(xiàn)Aware接口,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 帶你玩轉(zhuǎn)Kafka之初步使用

    帶你玩轉(zhuǎn)Kafka之初步使用

    最近開發(fā)的項(xiàng)目中,kafka用的比較多,為了方便梳理,所以記錄一些關(guān)于kafka的文章,這篇文章主要給大家介紹了關(guān)于Kafka初步使用的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • Java實(shí)現(xiàn)帶頭結(jié)點(diǎn)的單鏈表

    Java實(shí)現(xiàn)帶頭結(jié)點(diǎn)的單鏈表

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)帶頭結(jié)點(diǎn)的單鏈表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-09-09

最新評論

恭城| 光泽县| 海阳市| 黔江区| 海盐县| 清镇市| 武乡县| 永兴县| 西乡县| 青冈县| 衡东县| 阿克苏市| 合肥市| 福清市| 兰西县| 赞皇县| 连云港市| 南京市| 库车县| 富蕴县| 布尔津县| 和田县| 中西区| 呼伦贝尔市| 萨迦县| 惠州市| 临颍县| 睢宁县| 合水县| 察哈| 水城县| 五峰| 和龙市| 土默特右旗| 县级市| 五大连池市| 江安县| 齐齐哈尔市| 肇庆市| 福泉市| 庄河市|