在地图界面的entrySet()方法的Java文档中,我找到了此语句,但我真的不理解。
该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了地图,则迭代的结果为 undefined
此处的 undefined 是什么意思?
为了进一步澄清,这是我的情况。
我有一个基于spring和hibernate的Web应用程序。
我们的团队实现了名为CachedIntegrationClients的自定义缓存类。
我们正在使用RabbitMQ作为消息传递服务器。
而不是每次要向服务器发送消息时都获取客户端,而是使用先前的缓存类来缓存客户端。
问题是消息两次发送到消息传递服务器。
查看日志,我们发现获取缓存的客户端的方法将客户端返回两次,尽管(理论上)这是不可能的,因为我们将客户端存储在地图中,并且地图不允许重复的键。
在对代码进行抽烟之后,我发现对缓存的客户端进行迭代的方法从缓存的客户端映射中获取了一组客户端。
因此,我怀疑在遍历此集合时,另一个客户端会发出另一个请求,并且该客户端可能未缓存,因此它修改了地图。
无论如何,这就是CachedIntegrationClients类
公共类CachedIntegrationClientServiceImpl {
私有IntegrationDao integrationDao;
私有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]
最佳答案
你看过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()。实际上,如果我没记错的话,这实际上应该是一个永无止境的循环,因为一种方法调用了另一种方法,反之亦然。初始化期间永远不会满足!=空条件。当然,您正在以未定义的方式修改和迭代,因此也许可以使您摆脱无限循环。