изменение карты при переборе ее набора записей - PullRequest
3 голосов
/ 23 декабря 2010

В документах 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]

Ответы [ 2 ]

4 голосов
/ 23 декабря 2010

Не определено означает, что нет никаких требований для какого-либо конкретного поведения. Реализация бесплатна, чтобы начать Вторую Мировую Войну, заново повесить все рулоны туалетной бумаги с помощью обычного метода, запятнать вашу бабушку и т. Д.

Единственное разрешенное изменение с указанным поведением - через итератор.

2 голосов
/ 23 декабря 2010

Вы смотрели на 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 никогда не выполняется во время инициализации.Конечно, вы изменяете и повторяете неопределенным образом, так что, возможно, это спасает вас от бесконечного цикла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...