免费的网站管理系统,软装设计师年终总结,万户做网站怎么样,联通入股腾讯在对一些非并发集合同时进行读写的时候#xff0c;会抛出 ConcurrentModificationException
异常产生示例
示例一#xff08;单线程#xff09;#xff1a; 遍历集合时候去修改
抛出 ConcurrentModificationException 的主要原因是当你在遍历一个集合#xff08;如 Map…在对一些非并发集合同时进行读写的时候会抛出 ConcurrentModificationException
异常产生示例
示例一单线程 遍历集合时候去修改
抛出 ConcurrentModificationException 的主要原因是当你在遍历一个集合如 Map、List 或 Set时同时对该集合进行了结构性修改例如添加或删除元素而这些修改没有通过迭代器自身的相应方法进行。 private static MapString, String map new HashMap();static {for (int i 0; i 100000; i) {map.put(String.valueOf(i), String.valueOf(i));}}public static void main(String[] args) {// 在循环的时候删除元素map.forEach((key, value) - {if (Integer.parseInt(key) % 2 0){map.remove(key);return;}System.out.println(key value);});}示例二多线程 多线程读写
多线程读写和示例一抛出异常的原因一样 private static MapString, String map new HashMap();static {for (int i 0; i 100000; i) {map.put(String.valueOf(i), String.valueOf(i));}}public static void main(String[] args) {// 并发修改 map 让其出现并发异常for (int i 0; i 100; i) {new Thread(new Add(map)).start();new Thread(new Read(map)).start();}}static class Add implements Runnable {private HashMapString, String map;public Add(HashMapString, String map) {this.map map;}Overridepublic void run() {map.clear();for (int i 0; i 100000; i) {map.put(String.valueOf(i), String.valueOf(i));}}}static class Read implements Runnable {private HashMapString, String map;public Read(HashMapString, String map) {this.map map;}Overridepublic void run() {CopyOnWriteArraySetMap.EntryString,String copyOnWriteArraySet new CopyOnWriteArraySet(map.entrySet());for (int i 0; i 100000; i) {copyOnWriteArraySet.forEach((entry) - {String a entry.getKey() entry.getValue();});}}}解决问题
解决示例一
CopyOnWriteArraySet
CopyOnWriteArraySet 是 Java 并发集合包中的一种线程安全的集合。它的关键特性在于“写时复制”copy-on-write这意味着在对集合进行修改操作如添加或删除元素时其实现机制是创建底层数组的一个新副本而不是直接在原数组上进行修改。这种机制使得在遍历 CopyOnWriteArraySet 时不会抛出 ConcurrentModificationException因为迭代器是在数组的一个快照上工作的它不受后续对集合进行的修改的影响。
CopyOnWriteArraySetMap.EntryString,String copyOnWriteArraySet new CopyOnWriteArraySet(map.entrySet());
copyOnWriteArraySet.forEach((entry) - {String key entry.getKey();String value entry.getValue();if (Integer.parseInt(key) % 2 0){map.remove(key);return;}System.out.println(key value);
});迭代器
当使用迭代器遍历集合时直接调用集合的 remove 方法如 map.remove(key)会导致 ConcurrentModificationException因为这破坏了迭代器预期的遍历顺序。而迭代器自身提供的 remove 方法是唯一一种可以在遍历过程中安全移除元素的方法。该方法确保了在删除元素后迭代器能够继续正确地跟踪集合的状态不会导致并发修改异常。
IteratorMap.EntryString, String iterator map.entrySet().iterator();
while (iterator.hasNext()) {Map.EntryString, String entry iterator.next();if (Integer.parseInt(entry.getKey()) % 2 0) {iterator.remove(); // 使用迭代器的remove方法来安全地移除元素} else {System.out.println(entry.getKey() entry.getValue());}
}替换原本的集合
map map.entrySet().stream().filter(entry - Integer.parseInt(entry.getKey()) % 2 ! 0).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) - v1, HashMap::new));包装map
ConcurrentHashMapString, String concurrentHashMap new ConcurrentHashMap(map);
concurrentHashMap.forEach((key, value) - {if (Integer.parseInt(key) % 2 0){map.remove(key);return;}System.out.println(key value);
});方式二也是替换 Map
ConcurrentHashMapString, String concurrentHashMap new ConcurrentHashMap(map);
concurrentHashMap.forEach((key, value) - {if (Integer.parseInt(key) % 2 0){concurrentHashMap .remove(key);return;}System.out.println(key value);
});
map concurrentHashMap 使用 removeIf map.entrySet().removeIf(f- Integer.parseInt(f.getKey()) % 2 0);解决示例二
HashMap 本身并不是线程安全的最直接有效的方法是直接还一个线程安全的集合 Java 提供了多种线程安全的集合它们主要通过不同的方式来支持并发操作。以下是一些常见的线程安全集合 1. 基于 java.util.concurrent 包的线程安全集合
这些集合在高并发场景下性能较好适用于大多数现代应用。
1.1 ConcurrentHashMap
特性线程安全的哈希表支持高效的并发读写。优势读操作无锁写操作基于分段锁Java 8 后使用 CAS。用法ConcurrentHashMapString, String map new ConcurrentHashMap();1.2 CopyOnWriteArrayList
特性在写操作时复制整个底层数组因此适合读多写少的场景。优势读操作无锁写操作创建新数组避免并发问题。用法CopyOnWriteArrayListString list new CopyOnWriteArrayList();1.3 CopyOnWriteArraySet
特性基于 CopyOnWriteArrayList 实现适合高频读取、低频修改的场景。用法CopyOnWriteArraySetString set new CopyOnWriteArraySet();1.4 ConcurrentLinkedQueue
特性基于链表的无界非阻塞线程安全队列。优势采用 CAS 操作适合高并发环境下的队列操作。用法ConcurrentLinkedQueueString queue new ConcurrentLinkedQueue();1.5 ConcurrentLinkedDeque
特性双端队列版本的 ConcurrentLinkedQueue支持高效的双向操作。用法ConcurrentLinkedDequeString deque new ConcurrentLinkedDeque();1.6 LinkedBlockingQueue
特性基于链表的阻塞队列支持可选的容量限制。优势适合生产者-消费者模型。用法LinkedBlockingQueueString queue new LinkedBlockingQueue(100);1.7 LinkedBlockingDeque
特性双端队列版本的 LinkedBlockingQueue支持从两端进行操作。用法LinkedBlockingDequeString deque new LinkedBlockingDeque(100);1.8 ConcurrentSkipListMap
特性基于跳表的线程安全 SortedMap 实现支持按键排序。用法ConcurrentSkipListMapString, String map new ConcurrentSkipListMap();1.9 ConcurrentSkipListSet
特性基于 ConcurrentSkipListMap 实现的线程安全有序集合。用法ConcurrentSkipListSetString set new ConcurrentSkipListSet();2. 基于 Collections 工具类的同步集合
Collections.synchronizedXXX 方法可以将非线程安全的集合包装成线程安全集合但性能不如 java.util.concurrent 包。
2.1 SynchronizedList
特性将普通 List 包装成线程安全集合。用法ListString list Collections.synchronizedList(new ArrayList());2.2 SynchronizedSet
特性将普通 Set 包装成线程安全集合。用法SetString set Collections.synchronizedSet(new HashSet());2.3 SynchronizedMap
特性将普通 Map 包装成线程安全集合。用法MapString, String map Collections.synchronizedMap(new HashMap());注意使用 Collections.synchronizedXXX 包装的集合在迭代时需要显式加锁 synchronized (list) {for (String s : list) {// 操作}
}3. Immutable Collections不可变集合
Java 9 引入了不可变集合通过 List.of()、Set.of() 和 Map.of() 创建
特性线程安全不可修改适合配置类或常量类数据。用法ListString list List.of(a, b, c);
SetString set Set.of(a, b, c);
MapString, String map Map.of(key1, value1, key2, value2);4. Vector 和 Stack
这些是早期的线程安全集合
Vector线程安全的 List 实现。VectorString vector new Vector();Stack线程安全的栈继承自 Vector。StackString stack new Stack();但它们性能较低通常不推荐使用。 推荐选择
高并发场景优先使用 java.util.concurrent 包下的集合例如 ConcurrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet等。读多写少使用 CopyOnWriteArrayList 或 CopyOnWriteArraySet。简易同步使用 Collections.synchronizedXXX 包装集合。不可修改数据使用不可变集合List.of 等。
如果你有特定的应用场景可以详细讨论选择最优集合