7

Java Concurrency In Practice by Brian Goetz provides an example of a efficient scalable cache for concurrent use. Here is the code for the class:

public class Memoizer<A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
        = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

    public Memoizer(Computable<A, V> c) { this.c = c; }

    public V compute(final A arg) throws InterruptedException {
        while (true) {
            Future<V> f = cache.get(arg);
            if (f == null) {
                Callable<V> eval = new Callable<V>() {
                    public V call() throws InterruptedException {
                        return c.compute(arg);
                    }
                };
                FutureTask<V> ft = new FutureTask<V>(eval);
                f = cache.putIfAbsent(arg, ft);
                if (f == null) { f = ft; ft.run(); }
            }
            try {
                return f.get();
            } catch (CancellationException e) {
                cache.remove(arg, f);
            } catch (ExecutionException e) {
                throw launderThrowable(e.getCause());
            }
        }
    } }

Probably a stupid question but coudl anyone show me the concurrent usage of this class? Like in a main?

Cheers, Agata

Agata
  • 127
  • 3
  • 9
  • You could be also interested in the Google Guava MapMaker - http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/MapMaker.html . – Henryk Konsek Feb 03 '11 at 13:19

2 Answers2

6

Here is an example which calculates factorials:

public static void main(String[] args) throws Exception {

    //create a memoizer that performs factorials
    final Memoizer<Integer, Integer> memo = new Memoizer<Integer, Integer> (new Computable<Integer, Integer>() {
        @Override
        public Integer compute(Integer a) {
            int result = 1 ;
            for(int i = 1 ; i < a ; i++){
                result = result*i;
            }
            return result;
        }
    });

    //now call the memoizer
    System.out.println(memo.compute(10));


    //call it with 10 threads concurrently
    ExecutorService exec = Executors.newFixedThreadPool(10);
    ExecutorCompletionService<Integer> compService = new ExecutorCompletionService<Integer>(exec);
    for(int i = 0 ; i < 15 ; i++){
        compService.submit(new Callable<Integer>(){
            @Override
            public Integer call() throws Exception {
                return memo.compute(5);
            }
        });
    }
    exec.shutdown();
    for(int i = 0 ; i < 15 ; i++){
        System.out.println(compService.take().get());
    }
}

So if two threads try to compute the same factorial at exactly the same time, only one of them will actually perform the computation, because putIfAbsent is threadsafe. The second thread will simply get the future which was put in the map by the first thread and wait for it to finish.

dogbane
  • 266,786
  • 75
  • 396
  • 414
1

I could imagine something like this:

class PrimeDetector implements Computable<BigInteger, Boolean> {
  public Boolean compute(BigInteger number) {
    // detect whether the number is prime and return true if it is
  }
}

Memoizer<BigInteger, Boolean> primeMemoizer =
        new Memoizer<BigInteger, BigInteger[]>(new PrimeDetector());
boolean isPrime = primeMemoizer.compute(
        new BigInteger("5625945193217348954671586615478165774647538956473535"));
...
Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • Hello,Thank you for answering. This is what I came up with as well but what I don't understand is how it is used concurrently? I mean you created a PrimeDetector, put it in Memoizer but what if I have more objects like PrimeDetector and I want all of them use the Memorizer and compute in paraller. When I set one Computable object into Memorizer I won't be able to change it nor use it with different Computable objects in one time. This is what I don't understand :( – Agata Feb 03 '11 at 13:19
  • Maybe my explanation is not the most clear one.. Generally I don't understand how it should work concurrently. – Agata Feb 03 '11 at 13:24
  • What's the problem? You can use it for one Computable only, but you can test many primes concurrently. – maaartinus Feb 03 '11 at 13:29
  • @Agata, the idea is that you can share the memoizer instance between threads and all of them can use it concurrently to make computations. If you want to use (and cache results from) multiple `Computable` objects, you need to enclose each of them in a distinct `Memoizer` and publish each to your threads. – Péter Török Feb 03 '11 at 13:30