不在不同线程中重新评估昂贵的数据

Not reevaluating expensive data in different threads

我有这样的方法

public Object doSomethingExpensive(String x);

现在,如果我处理此方法,我可以将结果保存在 HashMap 中,例如,它们的键是字符串 x,值是结果对象。

如果该地图中存在数据,我就不必再次处理它。

但现在我几乎同时收到两个请求。 在这种情况下,我想让第二个请求等待,直到第一个请求完成,第二个请求在计算后也可以得到第一个请求的结果,所以我不必计算两次或并行两次。

重点是,我不会用

public synchronized Object doSomethingExpensive(String x);

因为如果 String x 是其他东西,则 Object 是其他东西。 所以我需要在那个字符串 x 上进行一些同步。

但是 synchronized(x) 是不可能的,因为 java 中的字符串文字...

此外,如果没有字符串而是一个对象作为 x,那么我可能会在第二个请求中得到一个与请求 1 相关的具有相同内容的类似对象,但它们各自是一些其他对象。

是的,所以我的问题是,如何解决这个问题,如何防止并行计算字符串 x 的结果两次,如何同步它并将结果缓存在 HashMap 中。

不知道我是否理解你的问题,如果是为了避免重复计算,这本好书(Java Concurrency in Practice)给出了一个解决方案的例子:

  private final Map<String, Future<Object>> cache
      = new ConcurrentHashMap<String, Future<Object>>();

  public Object doSomethingExpensive(String x) throws InterruptedException {
    while (true) {
      Future<Object> future = cache.get(x);
      if (future == null) {
        Callable<Object> callable = new Callable<Object>() {
          @Override
          public Object call() throws Exception {
            // doSomethingExpensive todo
            return new Object();
          }
        };
        FutureTask<Object> futureTask = new FutureTask<>(callable);
        future = cache.putIfAbsent(x, futureTask);
        if (future == null) {
          future = futureTask;
          futureTask.run();
        }
      }
      try {
        return future.get();
      } catch (CancellationException e) {
        cache.remove(x);
      } catch (ExecutionException e) {
        throw new RuntimeException(e.getCause());
      }
    }
  }

编辑: 来自评论,使用JAVA8#ConcurrentHashMap#computeIfAbsent,真的很方便:

    ConcurrentHashMap<String, Object> concurrentHashMap = new ConcurrentHashMap<>();

    public Object doSthEx(String key) {
        return concurrentHashMap.computeIfAbsent(key, new Function<String, Object>() {
            @Override
            public Object apply(String s) {
                // todo 
                return new Object();
            }
        });
    }

或使用一些库来获得评论中提到的更全面的功能:https://github.com/ben-manes/caffeine