Java 在迭代映射的入口集时修改映射

Java 在迭代映射的入口集时修改映射,java,nhibernate,spring,Java,Nhibernate,Spring,在映射接口的entrySet()方法的java文档中,我发现了这条语句,但我真的不理解它 集合由映射支持,因此对映射的更改将反映在集合中,反之亦然。如果在对集合进行迭代时修改映射,则迭代的结果是未定义的 这里未定义的是什么意思 为了进一步澄清,这是我的情况 我有一个基于spring和hibernate的web应用程序 我们的团队实现了名为CachedIntegrationClient的定制缓存类 我们使用RabbitMQ作为消息传递服务器 我们不是每次向服务器发送消息时都获取客户机,而是使用前面

在映射接口的entrySet()方法的java文档中,我发现了这条语句,但我真的不理解它

集合由映射支持,因此对映射的更改将反映在集合中,反之亦然。如果在对集合进行迭代时修改映射,则迭代的结果是未定义的

这里未定义的是什么意思

为了进一步澄清,这是我的情况

我有一个基于spring和hibernate的web应用程序

我们的团队实现了名为CachedIntegrationClient的定制缓存类

我们使用RabbitMQ作为消息传递服务器

我们不是每次向服务器发送消息时都获取客户机,而是使用前面的缓存类缓存客户机

问题是消息会被发送到消息服务器两次

查看日志时,我们发现获取缓存客户机的方法返回客户机两次,尽管这(理论上)是不可能的,因为我们将客户机存储在映射中,并且映射不允许重复密钥

在冒烟地查看代码之后,我发现迭代缓存客户机的方法从缓存客户机映射中获取一组客户机

所以我怀疑,在迭代这个集合时,另一个客户机发出了另一个请求,而这个客户机可能被取消缓存,因此它修改了映射

无论如何,这是CachedIntegrationClients类

公共类CachedIntegrationClientServiceImpl{ 私有集成dao集成dao; 私有集成服务集成服务


未定义意味着对任何特定行为都没有要求。该实现可以自由启动第三次世界大战,用上手法重新悬挂所有卫生纸,玷污你的祖母,等等

唯一允许的具有指定行为的修改是通过迭代器进行的。

您看过吗

编辑:我又看了一遍你的代码,这让我觉得很奇怪:

在fillCachedIntegrationClients()中,您有以下循环:

for (IntegrationClient integrationClient : allCachedIntegrationClients) {
        putOneIntegrationClientOnCache(integrationClient);
    }
但是PutonIntegrationClientIncache方法本身直接调用FillCachedIntegrationClient()

}


一定是出了什么问题。您正在调用FillCachedIntegrationClient()两次。事实上,如果我没有弄错的话,这应该是一个永无止境的循环,因为一个方法调用另一个,反之亦然。那个!=初始化期间从不满足null条件。当然,您正在以一种未定义的方式进行修改和迭代,因此这可能会将您从无限循环中解救出来。

thnx Bernard Marx获取提示。我只是想确保问题在并发中。检查下一个答案,请告诉我,地图是否可能返回条目两次。@Fanooos:总是准确地返回两次?或者它会随着测试用例的不同而变化吗?关于你提到的代码,我注意到了,它也发生了变化。关于地图中的副本,实际上我无法在我的机器或测试服务器上重现问题,但它发生在生产服务器上,因此,我编辑了上面的问题,添加了出现的日志和另一种方法。在上面出现的日志中,您会发现该服务将向3个订阅者发送消息,并且有一个订阅者存在两次。我还检查了数据库,以检查此客户端是否在服务中订阅了两次,但您不能解释为什么!=初始化期间从未满足null条件?如果映射为null,则在第一次调用fill方法之后,它将被实例化,并且在第二次调用中它不是null。这就是我的理解,除非我错过了什么。
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;
    }
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.");
    }
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]
for (IntegrationClient integrationClient : allCachedIntegrationClients) {
        putOneIntegrationClientOnCache(integrationClient);
    }
synchronized private void putOneIntegrationClientOnCache(IntegrationClient integrationClient){
fillCachedIntegrationClients(); // only fill cache if it is null , it will never refill cache
...