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

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

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

前言

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

異常概述

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

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

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

在我們的實(shí)際測(cè)試中,我們發(fā)現(xiàn)即使在單線程環(huán)境下,如果在遍歷過(guò)程中直接修改集合,也會(huì)拋出此異常。例如:

List<String> fruits = new ArrayList<>();
fruits.add("香蕉");
fruits.add("西瓜");
try {
    for (String fruit : fruits) {
        if (fruit.equals("香蕉")) {
            fruits.remove(fruit); // 這里會(huì)拋出ConcurrentModificationException
        }
    }
} catch (ConcurrentModificationException e) {
    System.out.println("異常信息: " + e.getMessage());
}

單線程環(huán)境下的異常分析

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

在單線程環(huán)境下,以下代碼會(huì)觸發(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); // 這里會(huì)拋出ConcurrentModificationException
    }
}

源碼分析

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

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

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

private class Itr implements Iterator<E> {
    int expectedModCount = modCount;
    public E next() {
        checkForComodification();
        // ...
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

在我們的測(cè)試中,我們還發(fā)現(xiàn)不僅List集合會(huì)出現(xiàn)這個(gè)問(wèn)題,Map集合同樣存在類似問(wèn)題:

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); // 這里會(huì)拋出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(); // 正確的方式
    }
}

在我們的測(cè)試代碼中,我們驗(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));

在我們的測(cè)試中,這種方法同樣有效:

List<String> fruits = new ArrayList<>();
fruits.add("香蕉");
fruits.add("蘋(píng)果");
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í)際測(cè)試中,我們創(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("讀取線程開(kāi)始遍歷");
            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); // 等待讀取線程開(kāi)始
            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等非線程安全集合存在以下問(wèn)題:

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

解決方案對(duì)比

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

在我們的測(cè)試中,我們對(duì)幾種主要的解決方案進(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<>();
// 可以安全地在遍歷過(guò)程中修改
for (String item : copyOnWriteList) {
    System.out.println("當(dāng)前元素: " + item);
    copyOnWriteList.add("New Item"); // 不會(huì)拋出異常
}

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());
}
// 安全遍歷,不會(huì)拋出ConcurrentModificationException
while(safeIterator.hasNext()) {
    String item = safeIterator.next();
    // 處理元素...
}

在我們的實(shí)際測(cè)試中,我們發(fā)現(xiàn)這種方案在特定場(chǎng)景下非常有效:

public static void perform() {
    Iterator<String> iterator;
    synchronized(list) {
        iterator = new CopiedIterator<>(list.iterator());
    }
    System.out.println("獲取到只讀迭代器,開(kāi)始遍歷");
    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)

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

缺點(diǎn)

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

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

高級(jí)解決方案

1. ConcurrentHashMap

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

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

在我們的測(cè)試中,我們驗(yàn)證了ConcurrentHashMap的線程安全性:

private static void demoConcurrentHashMap() {
    Map<String, String> concurrentMap = new ConcurrentHashMap<>();
    concurrentMap.put("key1", "value1");
    concurrentMap.put("key2", "value2");
    // 測(cè)試ConcurrentHashMap
    for (String key : concurrentMap.keySet()) {
        if (key.equals("key2")) {
            concurrentMap.put("key4", "value4"); // 不會(huì)拋出異常
            System.out.println("添加了新鍵值對(duì): key4=value4");
        }
    }
    System.out.println("ConcurrentHashMap最終大小: " + concurrentMap.size());
}

2. CopyOnWriteArrayList/Set

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

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

我們的測(cè)試代碼驗(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"); // 不會(huì)拋出異常
    }
    System.out.println("CopyOnWriteArraySet內(nèi)容: " + cowSet);
}

3. 快照技術(shù)

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

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

我們?cè)跍y(cè)試中也驗(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());

在我們的測(cè)試中,我們使用了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過(guò)濾元素:");
    List<String> filteredList = list.stream()
        .filter(item -> !item.equals("banana"))
        .collect(Collectors.toList());
    System.out.println("過(guò)濾后: " + 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);
}

性能測(cè)試與對(duì)比

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

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

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

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

private static void performanceTest() {
    // 準(zhǔn)備大數(shù)據(jù)集
    List<String> largeList = new ArrayList<>();
    for (int i = 0; i < 10000; i++) {
        largeList.add("Item-" + i);
    }
    // 測(cè)試普通Iterator
    long startTime = System.nanoTime();
    Iterator<String> normalIterator = largeList.iterator();
    int count = 0;
    while (normalIterator.hasNext()) {
        normalIterator.next();
        count++;
    }
    long normalTime = System.nanoTime() - startTime;
    // 測(cè)試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額外開(kāi)銷: " + (copiedTime - normalTime) / 1000000.0 + " 毫秒");
}

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

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

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

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

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

fail-fast機(jī)制原理

Java集合框架中的fail-fast機(jī)制是一種錯(cuò)誤檢測(cè)機(jī)制,它能幫助開(kāi)發(fā)者盡早發(fā)現(xiàn)程序中的并發(fā)修改問(wèn)題。當(dāng)多個(gè)線程對(duì)集合進(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都會(huì)增加。同時(shí),Iterator在創(chuàng)建時(shí)會(huì)保存當(dāng)前的modCount值作為expectedModCount。每次調(diào)用Iterator的next()方法時(shí),都會(huì)檢查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ī)制來(lái)避免異常的發(fā)生
  2. 捕獲并記錄:在無(wú)法避免異常的情況下,捕獲異常并記錄日志
  3. 優(yōu)雅降級(jí):提供備選方案,確保系統(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("檢測(cè)到并發(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ù)具體場(chǎng)景選擇合適的解決方案:

  1. 讀多寫(xiě)少場(chǎng)景
    • 推薦使用CopyOnWriteArrayList/CopyOnWriteArraySet
    • 適用于緩存、配置信息等場(chǎng)景
  2. 高并發(fā)讀寫(xiě)場(chǎng)景
    • 推薦使用ConcurrentHashMap
    • 適用于需要高并發(fā)訪問(wèn)的Map結(jié)構(gòu)
  3. 需要長(zhǎng)時(shí)間遍歷的場(chǎng)景
    • 推薦使用CopiedIterator或快照技術(shù)
    • 適用于需要對(duì)大量數(shù)據(jù)進(jìn)行復(fù)雜處理的場(chǎng)景
  4. 簡(jiǎn)單過(guò)濾或轉(zhuǎn)換場(chǎng)景
    • 推薦使用Stream API
    • 代碼簡(jiǎn)潔,可讀性強(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處理用戶會(huì)話
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處理長(zhǎng)時(shí)間運(yùn)行的任務(wù)
public class DataProcessor {
    private List<DataItem> dataItems;
    public void processLargeDataSet() {
        Iterator<DataItem> iterator;
        synchronized(dataItems) {
            iterator = new CopiedIterator<>(dataItems.iterator());
        }
        // 長(zhǎng)時(shí)間處理不會(huì)阻塞其他線程對(duì)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ī)訪問(wèn):ArrayList
    • 頻繁插入刪除:LinkedList
    • 需要排序:TreeSet/TreeMap
    • 唯一性要求:HashSet/HashMap
  3. 減少鎖競(jìng)爭(zhēng)
    • 縮小同步塊范圍
    • 使用讀寫(xiě)鎖分離讀寫(xiě)操作
    • 考慮使用無(wú)鎖數(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)行批量處理

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

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

多線程環(huán)境

  1. 選擇合適的線程安全集合
    • 讀多寫(xiě)少:CopyOnWriteArrayList/Set
    • 讀寫(xiě)頻率相近:Collections.synchronizedList + 同步塊
    • 高并發(fā)Map:ConcurrentHashMap
  2. 避免長(zhǎng)時(shí)間鎖定集合
    • 使用CopiedIterator或快照技術(shù)
    • 縮小同步塊范圍
  3. 考慮使用并發(fā)工具類
    • BlockingQueue系列
    • ConcurrentSkipListMap/Set

在我們的多線程測(cè)試中,我們發(fā)現(xiàn)CopyOnWriteArrayList在讀多寫(xiě)少的場(chǎng)景下表現(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("讀取線程開(kāi)始遍歷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("修改線程開(kāi)始修改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ù)訪問(wèn)模式選擇集合
    • 隨機(jī)訪問(wèn)多:ArrayList
    • 插入刪除多:LinkedList
    • 唯一性要求:HashSet/TreeSet
  2. 預(yù)估集合大小
    • 使用構(gòu)造函數(shù)指定初始容量
    • 避免頻繁擴(kuò)容
  3. 減少不必要的復(fù)制
    • 謹(jǐn)慎使用CopyOnWrite集合
    • 優(yōu)化CopiedIterator實(shí)現(xiàn)

在我們的測(cè)試中,我們發(fā)現(xiàn)對(duì)于大數(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 深度剖析開(kāi)發(fā)調(diào)試日志的解決方案的文章就介紹到這了,更多相關(guān)Java ConcurrentModificationException調(diào)試日志內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    這篇文章主要來(lái)和大家探討一下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ì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 帶你玩轉(zhuǎn)Kafka之初步使用

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

    最近開(kāi)發(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

最新評(píng)論

新昌县| 连云港市| 石家庄市| 施秉县| 通化市| 奉贤区| 乳山市| 留坝县| 大同市| 河间市| 天气| 东乌珠穆沁旗| 南澳县| 宜宾市| 龙南县| 南昌县| 宁德市| 太湖县| 乌拉特中旗| 丹寨县| 茌平县| 大港区| 长阳| 小金县| 东海县| 鄂托克旗| 清苑县| 皋兰县| 汝城县| 盘山县| 万盛区| 金坛市| 四川省| 贵南县| 长垣县| 同德县| 奇台县| 武安市| 北安市| 双峰县| 南江县|