В документах java метода entrySet () интерфейса карты я нашел это утверждение и действительно не понимаю его.
Набор опирается на карту, поэтому изменения в карте отражаются в наборе, и наоборот. Если карта изменяется во время выполнения итерации по набору, то результаты итерации будут undefined
что подразумевается под undefined здесь?
Для большей ясности, это моя ситуация.
У меня есть веб-приложение на основе Spring и Hibernate.
Наша команда реализовала пользовательский класс кэширования с именем CachedIntegrationClients.
Мы используем RabbitMQ в качестве сервера обмена сообщениями.
вместо того, чтобы получать клиентов каждый раз, когда мы хотим отправить сообщение на сервер, мы кэшируем клиентов, используя предыдущий класс кэширования.
Проблема в том, что сообщения отправляются на сервер обмена сообщениями дважды.
Просматривая журналы, мы обнаружили, что метод получения кэшированных клиентов возвращает клиента дважды, хотя это (теоретически) невозможно, поскольку мы храним клиентов на карте, а карта не допускает дублирование ключей.
После некоторого просмотра кода я обнаружил, что метод, который перебирает кэшированные клиенты, получает набор клиентов из карты кэшированных клиентов.
Так что я подозревал, что во время итерации по этому набору другой клиент делает другой запрос, и этот клиент может быть не кэширован, поэтому он изменяет карту.
В любом случае, это класс CachedIntegrationClients
открытый класс CachedIntegrationClientServiceImpl {
частная интеграцияDao интеграцияDao;
частная интеграцияСервис интеграцииСервис;
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]