SynchronizedMap ConcurrentModificationException

时间:2018-01-18 14:15:40

标签: java multithreading concurrency synchronization

我正在尝试了解SynchronizedMap并运行以下代码。我得到以下输出异常。根据我的理解,当线程仍然在地图上执行写入或者线程处于休眠状态时,get()方法试图访问同步映射时会导致异常。我的理解是正确还是我错过了什么?

class MapHelper1 implements Runnable {
Map<String, Integer> map;

public MapHelper1(Map<String, Integer> map) {
    this.map = map;
    new Thread(this, "MapHelper1").start();
}

public void run() {
    map.put("One", 1);
    try {
        System.out.println("MapHelper1 sleeping");
        Thread.sleep(100);
    } catch (Exception e) {
        System.out.println(e);
    }

}
}

class MapHelper2 implements Runnable { 
Map<String, Integer> map;

public MapHelper2(Map<String, Integer> map) {
    this.map = map;
    new Thread(this, "MapHelper3").start();
}

public void run() {
    map.put("two", 1);
    try {
        System.out.println("MapHelper2 sleeping");
        Thread.sleep(100);
    } catch (Exception e) {
        System.out.println(e);
    }

}
}


class MapHelper3 implements Runnable {
Map<String, Integer> map;

public MapHelper3(Map<String, Integer> map) {
    this.map = map;
    new Thread(this, "MapHelper3").start();
}

public void run() {
    map.put("three", 1);
    try {
        System.out.println("MapHelper3 sleeping");
        Thread.sleep(100);
    } catch (Exception e) {
        System.out.println(e);
    }

}
}

public class MainClass {

public static void main(String[] args) {
    Map<String, Integer> hashMap = new HashMap<>();
    Map<String, Integer> syncMap = Collections.synchronizedMap(hashMap);
    MapHelper1 mapHelper1 = new MapHelper1(syncMap);
    MapHelper2 mapHelper2 = new MapHelper2(syncMap);
    MapHelper3 mapHelper3 = new MapHelper3(syncMap);



    for (Map.Entry<String, Integer> e : syncMap.entrySet()) {
        System.out.println(e.getKey() + "=" + e.getValue());
    }

}

}

输出:

MapHelper1 sleeping
MapHelper2 sleeping
MapHelper3 sleeping


Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1494)
at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1527)
at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1525)
at MainClass.main(MainClass.java:137)
Command exited with non-zero status 1

编辑:当生成输出时,我再次运行代码几次。为什么会出现这种情况?

2 个答案:

答案 0 :(得分:3)

迭代时不要同步访问权限。使用:

@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
            this.logger.debug("No failure URL set, sending 401 Unauthorized error");
            response.sendError(403, "Authentication Failed: " + exception.getMessage());
    }
}

甚至在synchronizedMap()方法javadoc

  

用户必须手动同步返回的内容   迭代任何集合视图时映射:

synchronized(syncMap) {
  for (Map.Entry<String, Integer> e : syncMap.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
  }
}

答案 1 :(得分:2)

我刚看完@Dariusz的回答,我认为这是完全正确的。

要具体回答为何看到随机行为,这完全是时机。也就是说,如果在主线程中迭代之前地图已被所有三个线程填充,那么一切都很好。但是如果一个线程尝试填充,而主线程中的迭代已经在进行中,那么就会得到异常。

是的,我知道这是出于演示目的。但在实际代码中,最好不要在构造函数中启动线程。