ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

为什么这段代码会抛出一个java ConcurrentModificationException?

2019-08-27 02:00:46  阅读:161  来源: 互联网

标签:java concurrency block synchronized


public final class ClientGateway {

   private static ClientGateway instance;
   private static List<NetworkClientListener> listeners = Collections.synchronizedList(new ArrayList<NetworkClientListener>());
   private static final Object listenersMutex = new Object();
   protected EventHandler eventHandler;


   private ClientGateway() {
      eventHandler = new EventHandler();
   }

   public static synchronized ClientGateway getInstance() {
      if (instance == null)
         instance = new ClientGateway();
      return instance;
   }

   public void addNetworkListener(NetworkClientListener listener) {
     synchronized (listenersMutex) {
        listeners.add(listener);
     }
   }


   class EventHandler {

     public void onLogin(final boolean isAdviceGiver) {
        new Thread() {
           public void run() {
              synchronized (listenersMutex) {
                 for (NetworkClientListener nl : listeners) 
                    nl.onLogin(isAdviceGiver);
              }
           }
        }.start();
     }

   }
}

此代码抛出ConcurrentModificationException
但是我想如果它们在listenerMutex上同步,那么它们应该是串行执行的吗?在侦听器列表上运行的函数内的所有代码都在同步在Mutex上的同步块中运行.修改列表的唯一代码是addNetworkListener(…)和removeNetworkListener(…),但此时从不调用removeNetworkListener.

错误似乎正在发生的是,当onLogin函数/线程正在迭代侦听器时,仍在添加NetworkClientListener.

感谢您的见解!

编辑:NetworkClientListener是一个接口,并将“onLogin”的实现留给实现该函数的编码器,但是它们的函数实现无法访问侦听器List.

此外,我只是完全重新检查,并且没有修改addNetworkListener()和removeNetworkListener()函数之外的列表,其他函数只迭代列表.更改代码:

for (NetworkClientListener nl : listeners) 
   nl.onLogin(isAdviceGiver);

至:

for(int i = 0; i < listeners.size(); i++)
   nl.onLogin(isAdviceGiver);

似乎解决了并发问题,但我已经知道这一点,并且想知道首先是什么导致它.

再次感谢您的继续帮助!

例外:
线程“Thread-5”中的异常java.util.ConcurrentModificationException
    at java.util.ArrayList $Itr.checkForComodification(ArrayList.java:782)
    at java.util.ArrayList $Itr.next(ArrayList.java:754)
    at chapchat.client.networkcommunication.ClientGateway $EventHandler $5.run(ClientGateway.java:283)

编辑好吧,我觉得有点愚蠢.但是,谢谢你的帮助!特别是MJB& jprete!

答:某人的onLogin()实现为网关添加了一个新的监听器.因此(因为java的同步是基于线程并且是可重入的,因此线程可能无法自行锁定)当onLogin()被调用时,我们在他的实现中,我们正在迭代监听器并在这样做的过程中,添加一个新听众.

解决方案:MJB建议使用CopyOnWriteArrayList而不是同步列表

解决方法:

互斥锁只能防止来自多个线程的访问.如果nl.onLogin()恰好具有向侦听器列表添加侦听器的逻辑,则可能抛出ConcurrentModificationException,因为它被(通过迭代器)访问并同时更改(通过添加).

编辑:一些更多的信息可能会有所帮助.我记得,Java集合通过保留每个集合的修改计数来检查并发修改.每次执行更改集合的操作时,计数都会增加.为了检查操作的完整性,在操作的开始和结束时检查计数;如果计数发生更改,则集合会在访问点处抛出ConcurrentModificationException,而不是在修改时抛出.对于迭代器,它会在每次调用next()之后检查计数器,因此在通过侦听器循环的下一次迭代中,您应该看到异常.

标签:java,concurrency,block,synchronized
来源: https://codeday.me/bug/20190827/1735960.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有