zoukankan      html  css  js  c++  java
  • 一次 java.util.ConcurrentModificationException 问题的fix

    其他写的比较好的文章:https://www.cnblogs.com/snowater/p/8024776.html

    我在一次多线程读写map的时候,然后再遍历的时候也遇到了该问题。

    现场代码

    private ConcurrentHashMap<Long, Set<Long>> m = new ConcurrentHashMap<>();
    
    // 多线程运行中
    public void test(Long p, Long value) {
        Set<Long> s = new HashSet<>();
        if (m.contains(p)) {
            s = m.get(p);
            s.add(value);
        } else {
            s.add(value);
        }
        m.put(p, s);
        for (Long id: s) {
            logger.info("" + id);
        }
    }
    

    可以看到,我是在多线程的读写一个线程安全的Map,但我用一个Set去读的map,这个Set可不是线程安全的,再之后的遍历Set的时候,就报了 java.util.ConcurrentModificationException 的错。

    分析

    我们先看看这个错误是什么,下面是源码

     /**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;
    
    final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }
    

    这个是源代码,我们可以看到里面有个modCount来表示修改次数,每次对HashMap的操作都会增加modCount,如果在遍历的时候,发现当前的modCount和遍历的modCount不一致的时候,就会报错。

    在我遇到的场景,Set的底层实际上就是用的Map的Key来做实现的,我的Set并不是一个线程安全的,而且还是一个浅拷贝(指向同一个地址),所以在多线程遍历Set的时候,会出现modCount不一致的问题,导致报错。

    解决办法

    因为避免不了浅拷贝,所以我的解决办法是将set替换成线程安全的,例如 ConcurrentHashMap,也可以是线程安全的Collection。

    其他情况的解决办法

    将报错的容器替换成线程安全的,例如万能的 ConcurrentHashMap;关注任何map的修改操作,可以试着将修改操作加锁。

  • 相关阅读:
    linux 短信收发
    sama5d3 环境检测 adc测试
    【Codeforces 723C】Polycarp at the Radio 贪心
    【Codeforces 723B】Text Document Analysis 模拟
    【USACO 2.2】Preface Numbering (找规律)
    【Codeforces 722C】Destroying Array (数据结构、set)
    【USACO 2.1】Hamming Codes
    【USACO 2.1】Healthy Holsteins
    【USACO 2.1】Sorting A Three-Valued Sequence
    【USACO 2.1】Ordered Fractions
  • 原文地址:https://www.cnblogs.com/qscqesze/p/14044452.html
Copyright © 2011-2022 走看看