在迭代其entrySet时修改映射

时间:2010-12-23 09:36:01

标签: java nhibernate spring

在map接口的entrySet()方法的java文档中,我找到了这个语句,我真的不理解它。

  

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

这里的 undefined 是什么意思?

有关更多说明,这是我的情况。

我有一个基于spring& amp;的web应用程序。冬眠。

我们的团队实现了名为CachedIntegrationClients的自定义缓存类。

我们正在使用RabbitMQ作为消息服务器。

每次我们想要向服务器发送消息时,我们不是让我们的客户端,而是使用先前的缓存类来缓存客户端。

问题是消息被发送到消息传递服务器两次。

查看日志,我们发现获取缓存客户端的方法会返回客户端两次,尽管这(理论上)不可能,因为我们将客户端存储在地图中,并且地图不允许重复键。

在查看代码后,我发现迭代缓存客户端的方法从缓存的客户端映射中获取一组客户端。

所以我怀疑在迭代这个集合时,另一个客户端发出了另一个请求,并且该客户端可能未被缓存,因此它会修改地图。

这是CachedIntegrationClients类

  

public class CachedIntegrationClientServiceImpl {       私有的整合DaoDao;       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;
}

}

这是迭代集合

的方法
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]

2 个答案:

答案 0 :(得分:4)

未定义表示不需要任何特定行为。实施是免费启动WWIII,通过上手方法重新挂起所有的卫生纸卷,玷污你的祖母等。

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

答案 1 :(得分:2)

你看过java.concurrent.ConcurrentHashMap吗?

编辑:我再次查看你的代码,这让我感到奇怪:

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

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

但putOneIntegrationClientOnCache方法本身直接调用fillCachedIntegrationClients();

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

}

必须出错的地方。您正在调用fillCachedIntegrationClients()两次。实际上,如果我没有弄错,这实际上应该是一个永无止境的循环,因为一种方法调用另一种方法,反之亦然。初始化期间永远不会满足!= null条件。当然,你正在以一种未定义的方式进行修改和迭代,这样可能会使你免于无限循环。