简体   繁体   English

在迭代其entrySet时修改映射

[英]modifying a map while iterating over its entrySet

In the java docs of the map interface's entrySet() method I found this statement and I really do no understand it. 在map接口的entrySet()方法的java文档中,我找到了这个语句,我真的不明白它。

The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. 该集由地图支持,因此对地图的更改将反映在集中,反之亦然。 If the map is modified while an iteration over the set is in progress, the results of the iteration are undefined 如果在对集合进行迭代时修改了映射,则迭代的结果是未定义的

what is meant by undefined here? 这里的undefined是什么意思?

For more clarification, this is my situation. 为了更加澄清,这是我的情况。

I have a web application based on spring & hibernate. 我有一个基于spring和hibernate的web应用程序。

Our team implemented custom caching class called CachedIntegrationClients. 我们的团队实现了名为CachedIntegrationClients的自定义缓存类。

We are using RabbitMQ as a messaging server. 我们使用RabbitMQ作为消息服务器。

instead of getting our clients each time we want to send a message to the server, we cache the clients using the previous caching class. 每次我们想要向服务器发送消息时,我们不是让我们的客户端,而是使用先前的缓存类来缓存客户端。

The problem is that the messages are sent to the messaging server twice. 问题是消息被发送到消息传递服务器两次。

Viewing the logs, we found that the method that get the cached clients return the client twice, although this (theoretically) impossible as we store the clients in a map, and the map does not allow duplicate keys. 查看日志,我们发现获取缓存客户端的方法返回客户端两次,虽然这(理论上)不可能,因为我们将客户端存储在映射中,并且映射不允许重复键。

After some smoke viewing of the code I found that the method that iterates over the cached clients gets a set of the clients from the cached clients map. 在查看代码后,我发现迭代缓存客户端的方法从缓存的客户端映射中获取一组客户端。

So I suspected that while iterating over this set, another request is made by another client and this client may be uncached, so it modifies the map. 因此我怀疑在迭代此集合时,另一个客户端发出了另一个请求,并且此客户端可能未缓存,因此它会修改映射。

Any way this is the CachedIntegrationClients class 这是CachedIntegrationClients类的任何方式

public class CachedIntegrationClientServiceImpl { private IntegrationDao integrationDao; public class CachedIntegrationClientServiceImpl {private IntegrationDao integrationDao; private IntegrationService integrationService; private IntegrationService integrationService;

Map<String, IntegrationClient> cachedIntegrationClients = null;

@Override
public void setBaseDAO(BaseDao baseDao) {
    super.setBaseDAO(integrationDao);
}

@Override
public void refreshCache() {
    cachedIntegrationClients = null;
}

synchronized private void putOneIntegrationClientOnCache(IntegrationClient integrationClient){
    fillCachedIntegrationClients(); // only fill cache if it is null , it will never refill cache
    if (! cachedIntegrationClients.containsValue(integrationClient)) {
        cachedIntegrationClients.put(integrationClient.getClientSlug(),integrationClient);
    }
}

/**
 * only fill cache if it is null , it will never refill cache
 */
private void fillCachedIntegrationClients() {
    if (cachedIntegrationClients != null) {
        return ;
    }
    log.debug("filling cache of cachedClients");
    cachedIntegrationClients = new HashMap<String, IntegrationClient>(); // initialize cache Map
    List<IntegrationClient> allCachedIntegrationClients= integrationDao.getAllIntegrationClients();
    if (allCachedIntegrationClients != null) {
        for (IntegrationClient integrationClient : allCachedIntegrationClients) {
            integrationService
                    .injectCssFileForIntegrationClient(integrationClient);
            fetchClientServiceRelations(integrationClient
                    .getIntegrationClientServiceList());
        }
        for (IntegrationClient integrationClient : allCachedIntegrationClients) {
            putOneIntegrationClientOnCache(integrationClient);
        }
    }
}

/**
 * fetch all client service
 * @param integrationClientServiceList
 */
private void fetchClientServiceRelations(
        List<IntegrationClientService> integrationClientServiceList) {
    for (IntegrationClientService integrationClientService : integrationClientServiceList) {
        fetchClientServiceRelations(integrationClientService);
    }
}

private void fetchClientServiceRelations(IntegrationClientService clientService) {
    for (Exchange exchange : clientService.getExchangeList()) {
        exchange.getId();
    }
    for (Company company : clientService.getCompanyList()) {
        company.getId();
    }

}
/**
 * Get a client given its slug.
 * 
 * If the client was not found, an exception will be thrown.
 * 
 * @throws ClientNotFoundIntegrationException
 * @return IntegrationClient
 */
@Override
public IntegrationClient getIntegrationClient(String clientSlug) throws ClientNotFoundIntegrationException {
    if (cachedIntegrationClients == null) {
        fillCachedIntegrationClients();
    }

    if (!cachedIntegrationClients.containsKey(clientSlug)) {
        IntegrationClient integrationClient = integrationDao.getIntegrationClient(clientSlug);
        if (integrationClient != null) {
            this.fetchClientServiceRelations(integrationClient.getIntegrationClientServiceList());
            integrationService.injectCssFileForIntegrationClient(integrationClient);
            cachedIntegrationClients.put(clientSlug, integrationClient);
        }
    }

    IntegrationClient client = cachedIntegrationClients.get(clientSlug);
    if (client == null) {
        throw ClientNotFoundIntegrationException.forClientSlug(clientSlug);
    }
    return client;
}

public void setIntegrationDao(IntegrationDao integrationDao) {
    this.integrationDao = integrationDao;
}

public IntegrationDao getIntegrationDao() {
    return integrationDao;
}

public Map<String, IntegrationClient> getCachedIntegrationClients() {
    if (cachedIntegrationClients == null) {
        fillCachedIntegrationClients();
    }
    return cachedIntegrationClients;
}

public IntegrationService getIntegrationService() {
    return integrationService;
}

public void setIntegrationService(IntegrationService integrationService) {
    this.integrationService = integrationService;
}

} }

and here is the method that iterates over the set 这是迭代集合的方法

public List<IntegrationClientService> getIntegrationClientServicesForService(IntegrationServiceModel service) {
        List<IntegrationClientService> integrationClientServices = new ArrayList<IntegrationClientService>();
        for (Entry<String, IntegrationClient> entry : cachedIntegrationClientService.getCachedIntegrationClients().entrySet()) {
            IntegrationClientService integrationClientService = getIntegrationClientService(entry.getValue(), service);
            if (integrationClientService != null) {
                integrationClientServices.add(integrationClientService);
            }
        }
        return integrationClientServices;
    }

Also here is the method that calls the previous one 这里也是调用前一个方法的方法

List<IntegrationClientService> clients = integrationService.getIntegrationClientServicesForService(service);
    System.out.println(clients.size());
    if (clients.size() > 0) {
        log.info("Inbound service message [" + messageType.getKey() + "] to be sent to " + clients.size()
                + " registered clients: [" + StringUtils.arrayToDelimitedString(clients.toArray(), ", ") + "]");

        for (IntegrationClientService integrationClientService : clients) {
            Message<T> message = integrationMessageBuilder.build(messageType, payload, integrationClientService);
            try {
                channel.send(message);
            } catch (RuntimeException e) {
                messagingIntegrationService.handleException(e, messageType, integrationClientService, payload);
            }
        }
    } else {
        log.info("Inbound service message [" + messageType.getKey() + "] but no registered clients, not taking any further action.");
    }

and here is the logs that appears on the server 这是服务器上显示的日志

BaseIntegrationGateway.createAndSendToSubscribers(65) | Inbound service message [news.create] to be sent to 3 registered clients: [Id=126, Service=IntegrationService.MESSAGE_NEWS, Client=MDC, Id=125, Service=IntegrationService.MESSAGE_NEWS, Client=CNBC, Id=125, Service=IntegrationService.MESSAGE_NEWS, Client=CNBC]

Undefined means there is no requirement for any specific behavior. 未定义意味着不需要任何特定行为。 The implementation is free to start WWIII, re-hang all your toilet rolls by the overhand method, sully your grandmother, etc. 实施是免费启动WWIII,通过上手方法重新挂起所有的卫生纸卷,玷污你的祖母等。

The only permitted modification with a specified behaviour is via the iterator. 具有指定行为的唯一允许修改是通过迭代器。

Have you looked at java.concurrent.ConcurrentHashMap ? 你看过java.concurrent.ConcurrentHashMap吗?

EDIT: I looked over your code again this stikes me as odd: 编辑:我再次查看你的代码,这让我感到奇怪:

In fillCachedIntegrationClients() you have the following loop: 在fillCachedIntegrationClients()中,您有以下循环:

for (IntegrationClient integrationClient : allCachedIntegrationClients) {
        putOneIntegrationClientOnCache(integrationClient);
    }

But the putOneIntegrationClientOnCache method itself directly calls fillCachedIntegrationClients(); 但putOneIntegrationClientOnCache方法本身直接调用fillCachedIntegrationClients();

synchronized private void putOneIntegrationClientOnCache(IntegrationClient integrationClient){
fillCachedIntegrationClients(); // only fill cache if it is null , it will never refill cache
...

} }

Something there must go wrong. 那里肯定有问题。 You are calling fillCachedIntegrationClients() twice. 您正在调用fillCachedIntegrationClients()两次。 Actually if I am not mistaken this should actually be a never-ending loop since one method calls the other and vice versa. 实际上,如果我没有弄错,这实际上应该是一个永无止境的循环,因为一种方法调用另一种方法,反之亦然。 The != null condition is never met during the initialization. 初始化期间永远不会满足!= null条件。 Ofcourse you are modifying and iterating in a undefined way so maybe that saves you from an infinite loop. 当然,你正在以一种未定义的方式修改和迭代,这样可能会使你从无限循环中解脱出来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM