V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nnegier
V2EX  ›  程序员

[接] 怎么用 Java 的并发类 ConcurrentHashMap 抛出 ConcurrentModificationException?

  •  1
     
  •   nnegier · 2022-10-15 16:33:25 +08:00 · 1248 次点击
    这是一个创建于 770 天前的主题,其中的信息可能已经有所发展或是发生改变。

    For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries. Similarly, Iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time.

    https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html

    经 V 友提醒,去看了下 javadoc ,然后发现了这句描述,于是决定复现:

    public class Main {
    
        private static ConcurrentHashMap<String,String> concurrentHashMap = new ConcurrentHashMap<>();
    
        public static void main(String[] args) {
    
            for(int i = 0 ; i < 100000 ; i++){
                concurrentHashMap.put("Key"+i,"Value"+i);
            }
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (Map.Entry<String, String> entry : concurrentHashMap.entrySet()){
                        System.out.println("1key:"+entry.getKey()+",value:"+entry.getValue());
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (Map.Entry<String, String> entry : concurrentHashMap.entrySet()){
                        System.out.println("2key:"+entry.getKey()+",value:"+entry.getValue());
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
    

    结果还是没有任何问题。然后我还有用线程在这个多线程遍历的同时去给其增加删除元素,量级也是 10 万,因为客户手机文件也差不多这个数量,结果还是没问题。

    所以我想问一下,有办法复现吗?

    第 1 条附言  ·  2022-10-16 00:09:14 +08:00
    原谅我的失发,我被这个问题折磨太久了,刚刚发现了一点思路,我比如我在用一个线程遍历这个并发类的时候,又有用另一个线程给这个重新赋值一个并发实例类,可能是这个问题,但我目前依旧没有复现,只是准备先改下试试。
    5 条回复    2022-10-16 10:43:53 +08:00
    DonaldY
        1
    DonaldY  
       2022-10-15 17:04:52 +08:00
    ConcurrentHashMap 不会出现 ConcurrentModificationException 。

    代码里都没有抛这个异常。

    next 指针用 volatile 修饰的,可见。
    LeegoYih
        2
    LeegoYih  
       2022-10-15 17:35:13 +08:00
    HashMap 修改数据时会修改 modCount 的值,遍历时发现 modCount 不一致说明存在另一个线程在修改数据,所以会抛出 ConcurrentModificationException

    ConcurrentHashMap 文档上写了 They do not throw ConcurrentModificationException. 这也是符合预期的,如果别人一直在修改数据,我这边遍历一直报错,还让不让人看数据了。

    后面 However 只是解释了 iterators 设计之初是为了单个线程遍历的,所以对 ConcurrentHashMap 遍历、size, isEmpty 和 containsValue 等操作只试用于监控或估计的目的,不能用来当做流程控制 /程序控制
    Jooooooooo
        3
    Jooooooooo  
       2022-10-15 17:43:05 +08:00
    They do not throw ConcurrentModificationException.

    这不是 do not 吗? 你咋复现.
    nnegier
        4
    nnegier  
    OP
       2022-10-15 23:01:28 +08:00
    @DonaldY 那 CopyOnWriteArraySet 会抛这个 ConcurrentModificationException 异常吗?我去看了代码也没有这个异常。但确实我数据存放类里面有这个问题日志,还很多,上千次
    DonaldY
        5
    DonaldY  
       2022-10-16 10:43:53 +08:00
    @nnegier 看源码,CopyOnWriteArraySet 底层创建时候使用的是 CopyOnWriteArrayList 。CopyOnWriteArrayList 里面就有抛 ConcurrentModificationException

    public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1046 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:29 · PVG 03:29 · LAX 11:29 · JFK 14:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.