Home > Blockchain >  Not reevaluating expensive data in different threads
Not reevaluating expensive data in different threads

Time:11-13

i have such a method

public Object doSomethingExpensive(String x);

now if i processed this method i can save the result in a HashMap for example, they key is the String x and the value is the result Object.

If the data is present in this map, i dont have to process it again.

But now i get two requests in, at nearly the same time. And in this case i want to let the second request wait, till the first one is done and the second requests can also get the result of the first one after it is calculated, so i dont have to calculate it twice or parallel twice.

the point is, i can't use

public synchronized Object doSomethingExpensive(String x);

because Object is something other if String x is something other. So i need some synchronized on that String x.

But synchronized(x) isn't possible because string literals in java....

Also if there wouldn't be a String but an Object as x, then maybe i get in the second requests a similar Object with equal content related to request 1, but they are some other objects each.

Yeah so my question is, how to solve this, how can i prevent calculating for a String x its result twice in parallel, how can i synchronize it and caching the results in a HashMap for example.

CodePudding user response:

I don't know if I understand your problem, If it's to avoid repeated calculation, this great book(Java Concurrency in Practice) gives a example of a solution:

  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());
      }
    }
  }

EDIT: From comments, using JAVA8#ConcurrentHashMap#computeIfAbsent, really really convenient :

    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();
            }
        });
    }

Or use some library to get more comprehensive features as mentioned in comment:https://github.com/ben-manes/caffeine.

  • Related