改自《Java Concurrency in Practice》Listing 5.19. Final Implementation of Memoizer.
比我之前自己写的好多了,必备工具类,记下来留着以后常用
特点:
1.使用Future与ConcurrentMap#putIfAbsent(K key, V value)避免了多个线程重复计算
2.如果计算被取消或失败,会调用ConcurrentMap#remove(key, future)来避免Cache Pollution
- public interface Computable<K, V> {
- V compute(final K key) throws InterruptedException;
- }
public interface Computable<K, V> {
V compute(final K key) throws InterruptedException;
}
- public class ConcurrentCache<K, V> implements Computable<K, V> {
- private static final int DEFAULT_INITIAL_CAPACITY = 16;
- private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
- private static final float DEFAULT_LOAD_FACTOR = 0.75f;
- private final ConcurrentMap<K, Future<V>> cache;
- private final Computable<K, V> computable;
- public ConcurrentCache(Computable<K, V> computable) {
- this(computable, DEFAULT_INITIAL_CAPACITY, DEFAULT_CONCURRENCY_LEVEL);
- }
- public ConcurrentCache(Computable<K, V> computable, int initialCapacity, int concurrencyLevel) {
- this(computable, initialCapacity, DEFAULT_LOAD_FACTOR, concurrencyLevel);
- }
- public ConcurrentCache(Computable<K, V> computable, int initialCapacity, float loadFactor, int concurrencyLevel) {
- this.computable = computable;
- cache = new ConcurrentHashMap<K, Future<V>>(initialCapacity, loadFactor, concurrencyLevel);
- }
- @Override
- public V compute(final K key) throws InterruptedException {
- while (true) {
- Future<V> future = cache.get(key);
- if (future == null) {
- FutureTask<V> futureTask = new FutureTask<V>(new Callable<V>() {
- @Override
- public V call() throws Exception {
- return computable.compute(key);
- }
- });
- /**
- * V putIfAbsent(K key, V value)
- * 等效于以下代码,只不过以原子操作的方式执行
- * if (!map.containsKey(key))
- * return map.put(key, value);
- * else
- * return map.get(key);
- */
- future = cache.putIfAbsent(key, futureTask);
- if (future == null) {
- future = futureTask;
- futureTask.run();
- }
- }
- try {
- return future.get();
- } catch (InterruptedException e) {
- /**
- * boolean remove(Object key, Object value)
- * 等效于以下代码,只不过以原子操作的方式执行
- * if (map.containsKey(key) && map.get(key).equals(value)) {
- * map.remove(key);
- * return true;
- * } else {
- * return false;
- * }
- */
- cache.remove(key, future);
- throw e;
- } catch (CancellationException e) {
- cache.remove(key, future);
- } catch (ExecutionException e) {
- cache.remove(key, future);
- throw new RuntimeException(e.getCause());
- } catch (RuntimeException e) {
- cache.remove(key, future);
- throw e;
- }
- }
- }
- }
public class ConcurrentCache<K, V> implements Computable<K, V> {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private final ConcurrentMap<K, Future<V>> cache;
private final Computable<K, V> computable;
public ConcurrentCache(Computable<K, V> computable) {
this(computable, DEFAULT_INITIAL_CAPACITY, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentCache(Computable<K, V> computable, int initialCapacity, int concurrencyLevel) {
this(computable, initialCapacity, DEFAULT_LOAD_FACTOR, concurrencyLevel);
}
public ConcurrentCache(Computable<K, V> computable, int initialCapacity, float loadFactor, int concurrencyLevel) {
this.computable = computable;
cache = new ConcurrentHashMap<K, Future<V>>(initialCapacity, loadFactor, concurrencyLevel);
}
@Override
public V compute(final K key) throws InterruptedException {
while (true) {
Future<V> future = cache.get(key);
if (future == null) {
FutureTask<V> futureTask = new FutureTask<V>(new Callable<V>() {
@Override
public V call() throws Exception {
return computable.compute(key);
}
});
/**
* V putIfAbsent(K key, V value)
* 等效于以下代码,只不过以原子操作的方式执行
* if (!map.containsKey(key))
* return map.put(key, value);
* else
* return map.get(key);
*/
future = cache.putIfAbsent(key, futureTask);
if (future == null) {
future = futureTask;
futureTask.run();
}
}
try {
return future.get();
} catch (InterruptedException e) {
/**
* boolean remove(Object key, Object value)
* 等效于以下代码,只不过以原子操作的方式执行
* if (map.containsKey(key) && map.get(key).equals(value)) {
* map.remove(key);
* return true;
* } else {
* return false;
* }
*/
cache.remove(key, future);
throw e;
} catch (CancellationException e) {
cache.remove(key, future);
} catch (ExecutionException e) {
cache.remove(key, future);
throw new RuntimeException(e.getCause());
} catch (RuntimeException e) {
cache.remove(key, future);
throw e;
}
}
}
}