在map接口的entrySet()方法的java文档中,我找到了这个声明,但我真的不理解它的意思。
集合由map支持,因此对map的更改会反映在集合中,反之亦然。如果在迭代集合时修改了map,则迭代的结果是未定义的。这里的“未定义”是什么意思?
为了更好地说明问题,这是我的情况。
我有一个基于Spring和Hibernate的Web应用程序。
我们的团队实现了自定义缓存类CachedIntegrationClients。
我们使用RabbitMQ作为消息服务器。
我们通过使用先前的缓存类缓存客户端,而不是每次想要向服务器发送消息时获取客户端。
问题是消息被发送到消息服务器两次。
查看日志,我们发现获取缓存客户端的方法返回客户端两次,尽管这在理论上是不可能的,因为我们将客户端存储在一个map中,而map不允许重复的键。
经过一些代码调试,我发现迭代缓存客户端的方法从缓存客户端map中获取客户端的集合。
因此,我怀疑在迭代此集合时,另一个客户端发出请求,并且此客户端可能未被缓存,因此它修改了map。
无论如何,这是CachedIntegrationClients类:
集合由map支持,因此对map的更改会反映在集合中,反之亦然。如果在迭代集合时修改了map,则迭代的结果是未定义的。这里的“未定义”是什么意思?
为了更好地说明问题,这是我的情况。
我有一个基于Spring和Hibernate的Web应用程序。
我们的团队实现了自定义缓存类CachedIntegrationClients。
我们使用RabbitMQ作为消息服务器。
我们通过使用先前的缓存类缓存客户端,而不是每次想要向服务器发送消息时获取客户端。
问题是消息被发送到消息服务器两次。
查看日志,我们发现获取缓存客户端的方法返回客户端两次,尽管这在理论上是不可能的,因为我们将客户端存储在一个map中,而map不允许重复的键。
经过一些代码调试,我发现迭代缓存客户端的方法从缓存客户端map中获取客户端的集合。
因此,我怀疑在迭代此集合时,另一个客户端发出请求,并且此客户端可能未被缓存,因此它修改了map。
无论如何,这是CachedIntegrationClients类:
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]