【问题标题】:How to debug ConcurrentModificationException? [duplicate]如何调试 ConcurrentModificationException? [复制]
【发布时间】:2010-10-24 19:40:49
【问题描述】:

我遇到了 ConcurrentModificationException,通过查看它我看不出它发生的原因;抛出异常的区域和所有修改集合的地方都被

包围
synchronized (this.locks.get(id)) {
  ...
} // locks is a HashMap<String, Object>;

我试图捕捉讨厌的线程,但我所能确定的(通过在异常中设置断点)是抛出线程拥有监视器,而另一个线程(程序中有两个线程)休眠。


我应该如何进行?当您遇到类似的线程问题时,您通常会怎么做?

【问题讨论】:

    标签: java multithreading exception concurrency


    【解决方案1】:

    它可能与同步块无关。 ConcurrentModificationExceptions 经常发生在您在迭代集合的元素时修改集合。

    List<String> messages = ...;
    for (String message : messages) {
        // Prone to ConcurrentModificationException
        messages.add("A COMPLETELY NEW MESSAGE");
    }
    

    【讨论】:

      【解决方案2】:

      与之前的帖子类似,如果您删除条目,也会遇到同样的问题。 例如

      for(String message : messages) {
        if (condition(message))
           messages.remove(message);
      }
      

      另一个常见的例子是清理地图。

      这个特殊的问题可以使用一个迭代器显式地解决。

      for(Iterator<String> iter = messages.iterator(); iter.hasNext();) {
         String message = iter.next();
         if (condition(message))
             iter.remove(); // doesn't cause a ConcurrentModificationException 
      }
      

      【讨论】:

      • 我仍然在 iter.remove() 行上得到 ConcurrentModificationException。
      • @ErikB 你的问题解决了吗?怎么样?
      • @user309483 我在性能测试后使用了 CopyOnWriteArray,发现性能对我们的需求来说还不错。在一些地方,我们使用了 iftee 提到的 toRemoveSet 解决方案。
      • 我还发现某些集合的 iterator() 方法只是提供了原始列表的迭代器“视图”,因此我仍然收到错误消息。
      • AFAIK,总是提供视图而不是副本。对于 CopyOnWriteArrayXxx 它在修改时执行复制,并且迭代器看到原始。
      【解决方案3】:

      有时您的应用程序可能过于复杂,某些功能可能会产生过多的副作用。此外,也许另一个线程确实对该列表做错了,您无法轻松找到。

      对于我自己的问题,我编写了自己的列表系统,它委托另一个列表,一旦锁定,所有其他修改都会抛出 ConcurrentModificationException,因此错误的修改指令将在输出时出现异常。它还可以检测上述错误。

      导入 java.util.*; /** * 由 IntelliJ IDEA 创建。 * 用户:francoiscassistat * 日期:2010 年 6 月 12 日 * 时间:18:20:18 * * * 可锁定列表,用于调试列表上的 ConcurrentModificationException。 * 可以使用 setLocked(boolean) 打开/关闭锁。 * 锁定时,对列表或迭代器的所有写访问都会获得 ConcurrentModificationException。 * 简单用例: * * list.setLocked(true); * * for (Object o : list.iterator()) // 现在 this 不会得到 ConcurrentModificationException,导致这个的其他指令将抛出异常 * { ... } * * list.setLocked(false); */ 公共类 LockableList 实现 List { 受保护的类 LockableListIterator 实现 Iterator { 受保护的 Iterator 迭代器; 公共 LockableListIterator(Iterator 迭代器) { this.iterator = 迭代器; } 公共布尔 hasNext() { 返回迭代器.hasNext(); } 公共E下一个(){ 返回迭代器.next(); } 公共无效删除(){ 检查锁(); 迭代器.remove(); } } 受保护的类 LockableListListIterator 实现 ListIterator { 受保护的 ListIterator listIterator; 公共 LockableListListIterator(ListIterator listIterator) { this.listIterator = listIterator; } 公共布尔 hasNext() { 返回 listIterator.hasNext(); } 公共E下一个(){ 返回 listIterator.next(); } 公共布尔 hasPrevious() { 返回 listIterator.hasPrevious(); } 公共 E 以前的() { 返回 listIterator.previous(); } 公共 int nextIndex() { 返回 listIterator.nextIndex(); } 公共 int previousIndex() { 返回 listIterator.previousIndex(); } 公共无效删除(){ 检查锁(); listIterator.remove(); } 公共无效集(E e){ 检查锁(); listIterator.set(e); } 公共无效添加(E e){ 检查锁(); listIterator.add(e); } } 受保护的类 LockableListSubList 实现 List { 受保护的列表 列表; 公共 LockableListSubList(List 列表) { this.list = 列表; } 公共整数大小(){ 返回列表大小(); } 公共布尔 isEmpty() { 返回列表.isEmpty(); } 公共布尔包含(对象o){ 返回 list.contains(o); } 公共迭代器迭代器(){ 返回新的 LockableListIterator(list.iterator()); } 公共对象[] toArray() { 返回列表.toArray(); } 公共 T[] toArray(T[] a) { 返回 list.toArray(a); } 公共布尔添加(E e){ 检查锁(); 返回列表.add(e); } 公共布尔删除(对象o){ 检查锁(); 返回列表.remove(o); } public boolean containsAll(Collection> c) { 返回 list.containsAll(c); } public boolean addAll(Collection extends E> c) { 检查锁(); 返回列表.addAll(c); } public boolean addAll(int index, Collection extends E> c) { 检查锁(); 返回 list.addAll(index, c); } public boolean removeAll(Collection> c) { 检查锁(); 返回列表.removeAll(c); } 公共布尔保留所有(集合 c){ 检查锁(); 返回列表.retainAll(c); } 公共无效清除(){ 检查锁(); list.clear(); } @覆盖 公共布尔等于(对象o){ 返回 list.equals(o); } @覆盖 公共 int hashCode() { 返回 list.hashCode(); } 公共 E 获取(整数索引){ 返回列表.get(索引); } public E set(int index, E element) { 检查锁(); 返回 list.set(index, element); } 公共无效添加(int索引,E元素){ 检查锁(); list.add(索引,元素); } 公共 E 删除(整数索引){ 检查锁(); 返回列表。删除(索引); } 公共 int indexOf(对象 o){ 返回 list.indexOf(o); } 公共int lastIndexOf(对象o){ 返回 list.lastIndexOf(o); } 公共 ListIterator listIterator() { 返回新的 LockableListListIterator(list.listIterator()); } 公共 ListIterator listIterator(int index) { 返回新的 LockableListListIterator(list.listIterator(index)); } 公共列表 subList(int fromIndex, int toIndex) { return new LockableListSubList(list.subList(fromIndex, toIndex)); } } 受保护的列表 列表; 受保护的布尔锁定; 公共 LockableList(List 列表) { this.list = 列表; 锁定=假; } 公共布尔 isLocked() { 返回锁定; } 公共无效 setLocked(布尔锁定){ this.locked = 锁定; } 受保护的无效检查锁(){ 如果(锁定) throw new ConcurrentModificationException("Locked"); } 公共整数大小(){ 返回列表大小(); } 公共布尔 isEmpty() { 返回列表.isEmpty(); } 公共布尔包含(对象o){ 返回 list.contains(o); } 公共迭代器迭代器(){ 返回新的 LockableListIterator(list.iterator()); } 公共对象[] toArray() { 返回列表.toArray(); } 公共 T[] toArray(T[] a) { 返回 list.toArray(a); } 公共布尔添加(E e){ 检查锁(); 返回列表.add(e); } 公共布尔删除(对象o){ 检查锁(); 返回列表.remove(o); } public boolean containsAll(Collection> c) { 返回 list.containsAll(c); } public boolean addAll(Collection extends E> c) { 检查锁(); 返回列表.addAll(c); } public boolean addAll(int index, Collection extends E> c) { 检查锁(); 返回 list.addAll(index, c); } public boolean removeAll(Collection> c) { 检查锁(); 返回列表.removeAll(c); } 公共布尔保留所有(集合 c){ 检查锁(); 返回列表.retainAll(c); } 公共无效清除(){ 检查锁(); list.clear(); } @覆盖 公共布尔等于(对象o){ 返回 list.equals(o); } @覆盖 公共 int hashCode() { 返回 list.hashCode(); } 公共 E 获取(整数索引){ 返回列表.get(索引); } public E set(int index, E element) { 检查锁(); 返回 list.set(index, element); } 公共无效添加(int索引,E元素){ 检查锁(); list.add(索引,元素); } 公共 E 删除(整数索引){ 检查锁(); 返回列表。删除(索引); } 公共 int indexOf(对象 o){ 返回 list.indexOf(o); } 公共int lastIndexOf(对象o){ 返回 list.lastIndexOf(o); } 公共 ListIterator listIterator() { 返回新的 LockableListListIterator(list.listIterator()); } 公共 ListIterator listIterator(int index) { 返回新的 LockableListListIterator(list.listIterator(index)); } 公共列表 subList(int fromIndex, int toIndex) { return new LockableListSubList(list.subList(fromIndex, toIndex)); } }

      像这样简单地使用它:

      List list = new LockableList(new ArrayList(...)); list.setLocked(true); for (E e : list.iterator()) { ... } list.setLocked(false);

      希望它可以帮助别人。

      【讨论】:

        【解决方案4】:

        如果您需要从列表中删除一些元素。您可以维护另一个列表,例如要删除的元素。最后调用 removeAll(collection)。当然,这对于大数据是不利的。

        【讨论】:

          【解决方案5】:

          由于不得不处理类似的问题,我编写了一个小助手来调试某些对象上的并发访问情况(有时使用调试器会修改运行时行为,以至于问题不会发生)。该方法类似于弗朗索瓦展示的方法,但更通用一些。也许它可以帮助某人:http://code.google.com/p/kongcurrent/

          【讨论】:

            【解决方案6】:

            在对动态列表进行迭代时(例如在 foreach 循环中)修改动态列表时,通常会收到 ConcurrentModificationException。你可能想确保你没有在任何地方这样做。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-02-12
              • 2010-10-10
              • 2017-09-22
              • 2020-12-19
              相关资源
              最近更新 更多