HashMap迭代/删除获取java.util.ConcurrentModificationException

时间:2012-05-13 13:11:12

标签: java multithreading hashmap

我有一个ClientSocket和客户端对象的HashMap。

我正在迭代使用for循环抛出它们,但有时新的行被添加到hashmap然后我得到java.util.ConcurrentModificationException错误。 我理解为什么会发生,但我不明白如何解决它。我试图在迭代开始之前制作我的HashMap的新副本但是 - 我仍然得到了错误。

我的代码:

private volatile HashMap<ClientSocket, Client> clientsMap = new HashMap<ClientSocket, Client>();
private volatile HashMap<ClientSocket, Client> iteratorClientsMap = new HashMap<ClientSocket, Client>();
private volatile ClientsMapIterator iterator;

iterator = new ClientsMapIterator(clientsMap);
iteratorClientsMap = iterator.getItreator();

for (Map.Entry<ClientSocket, Client> entry : iteratorClientsMap.entrySet()) {                                                                   
    ClientSocket key = entry.getKey();
    //Client value = entry.getValue();              
    long diff = currentTime - key.getLastOnline();
    boolean isAvailable = false;

    try {
        isAvailable = (key.getSocket().getInputStream().available() > 0);
    } catch (IOException e) {
        e.printStackTrace();
    }               

    if ( diff > keepAlive)              
        removeClientSocket(key);
}

public synchronized void addClientSocket(ClientSocket clientSocket) {
    clientsMap.put(clientSocket, null);                 
}

addClientSocket是因为它我得到错误的函数。

3 个答案:

答案 0 :(得分:2)

您正在迭代它时修改集合。这被标记为并发修改。

最简单的解决方案是使用不触发CME的ConcurrentHashMap。

答案 1 :(得分:0)

我找到了一个解决方案,我不知道它是否是最好的解决方案:

synchronized (this) {
            iterateClientsMap = new HashMap<ClientSocket, Client>(clientsMap);  
        }           

        for (Map.Entry<ClientSocket, Client> entry : iterateClientsMap.entrySet())      
        {                                                                                                   
            ClientSocket key = entry.getKey();
            //Client value = entry.getValue();              
            long diff = currentTime - key.getLastOnline();
            boolean isAvailable = false;
            try {
                isAvailable = (key.getSocket().getInputStream().available() > 0);
            } catch (IOException e) {
                e.printStackTrace();
            }               
            if ( diff > keepAlive)              
                removeClientSocket(key);
}

我复制了我的HashMap并在副本上进行了迭代,在每次迭代过程之前,我将其阻塞到其他线程(使用同步标题),因此在复制过程中不会被中断

答案 2 :(得分:0)

问题似乎是从removeClientSocket(key);

传播的

似乎集合在被迭代时被修改。

解决此问题的一种方法是将迭代器传递给此方法

removeClientSocket(iterator, key);

通过调用iterator.remove()从此迭代器中删除键,而不是在迭代过程中从集合本身中删除。

看起来您的问题在于多个线程,您可以同步访问同一个锁,例如:

public void removeClientSocket(iterator, key){
    synchronized(clientMap){
      //now remove
    }
}

public void addClientSocket(ClientSocket clientSocket) {
    synchronized(clientsMap){
        clientsMap.put(clientSocket, null);    
    }             
}

或使用java.util.Concurrent包进行自动并发控制。您可以专门使用ConcurrentHashMap