В Java параллелизм на практике Брайан Гетц
Java Concurrency in Practice by Brian Goetz предоставляет пример эффективного масштабируемого кэша для параллельного использования. Вот код для класса:
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());
}
}
} }
вероятно, глупый вопрос, но coudl кто-нибудь покажет мне одновременное использование этого класса? Как в главном?
Ура, Агата!--2-->
2 ответов
вот пример, который вычисляет факториалы:
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());
}
}
поэтому, если два потока пытаются вычислить один и тот же факториал в одно и то же время, только один из них фактически выполнит вычисление, потому что putIfAbsent
- это многопотоковое исполнение. Второй поток просто получит будущее, которое было помещено в карту первым потоком, и дождется его завершения.
Я могу представить что-то вроде этого:
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"));
...