如何安全地清理并发地图

时间:2018-05-18 16:00:06

标签: java multithreading concurrency concurrenthashmap

我正在努力解决一个对我来说很常见的问题,但我找不到合适的解决方案。

在非常并发的环境中,我需要在销毁客户端会话时正确释放资源。这是输入:

  • 我使用ConcurrentHashMap存储所有已分配的资源,此处需要map来索引资源
  • 在会话被销毁的同时,有时会从挂起的任务中分配新的资源,我也希望最终解除分配

这是我目前的解决方案:

while (!resourceMap.isEmpty()) {
    Map<Integer, Resource> toDestroy = new HashMap<>(resourceMap);
    for (Resource resource : toDestroy.values()) {
        resource.destroy();
    }
    resourceMap.keySet().removeAll(toDestroy.keySet());
}

仅存在,因为ConcurrentHashMap#values#iterator并不总是将并发投放反映到resourceMap。我不喜欢这段代码而更喜欢类似队列的代码,但不幸的是ConcurrentMap没有提供这样的代码:

while ((Map.Entry<String, Resource> entry = resourceMap.removeAny()) != null) {
    entry.value().destroy();
}

我正在寻找类似于上面类似队列的代码的解决方案或任何替代解决此问题的方法。

2 个答案:

答案 0 :(得分:1)

  

我不喜欢这段代码并且更喜欢类似队列的代码,但遗憾的是ConcurrentMap不提供类似这样的代码......

我只是使用迭代器,但我又不是Java 8粉丝。

while (!resourceMap.isEmpty()) {
    Iterator<Resource> iterator = resourceMap.values().iterator();
    while (iterator.hasNext()) {
       Resource resource = iterator.next();
       iterator.remove();
       resource.destroy();
    }
}

重要的是要注意此模型中存在竞争条件。有人可以获得资源,去使用它,但同时它被这个线程摧毁。

答案 1 :(得分:0)

我知道您有许多资源需要销毁,并且将资源放入此映射是一种请求异步销毁资源的方法。

并且,您有一个工作线程来轮询此地图并销毁它在其中找到的所有内容。

由于您要“排队”项目以便对它们请求操作,因此您可以使用ExecutorService。 (或者根据队列和工作线程写一些东西,它总结了执行者服务)。

使用执行器,一个例子是

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ResourceDestroyer {
  ExecutorService es = Executors.newSingleThreadExecutor();
  //"destroy" will be executed by the worker thread
  public void destroyResource(Resource r) {
    es.submit(r::destroy);
  }
}