这是我的通行证。我还没有测试过这段代码,但我认为它能捕获大多数情况
public class RelationCollectionCache {
// properties
private final ConcurrentMap<Integer, Reference<Future<RelationCollection>>> cache =
new ConcurrentHashMap<Integer, Reference<Future<RelationCollection>>>();
// constructors
public RelationCollectionCache() {
}
// methods
public RelationCollection load(final Integer id) {
Reference<Future<RelationCollection>> reference = cache.get(id);
Future<RelationCollection> future = (reference == null) ? null : reference.get();
while (future == null) {
final Callable<RelationCollection> eval = new Callable<RelationCollection>() {
public RelationCollection call() throws Exception {
return compute(id);
}
};
final FutureTask<RelationCollection> task = new FutureTask<RelationCollection>(eval);
final SoftReference<Future<RelationCollection>> newReference =
new SoftReference<Future<RelationCollection>>(task);
// Need to use replace as we may have an expired reference in the cache.
if (cache.replace(id, reference, newReference)) {
task.run();
future = task;
} else {
// Another thread may have done the replace
reference = cache.get(id);
future = (reference != null) ? reference.get() : null;
}
}
return getFromFuture(future);
}
private RelationCollection compute(final Integer id) {
final RelationCollection collection = new RelationCollection();
// lengthy computation of a large collection
return collection;
}
public RelationCollection get(final Integer id) {
Reference<Future<RelationCollection>> reference = cache.get(id);
if (reference == null) {
return null;
}
Future<RelationCollection> future = reference.get();
if (future == null) {
// Clean up the expired reference
while (!cache.remove(id, reference)) {
reference = cache.get(id);
future = (reference == null) ? null : reference.get();
// Its possible another thread may have replaced the
// expired reference with a valid in the mean time.
if (future != null) {
return getFromFuture(future);
}
}
return null;
}
return getFromFuture(future);
}
private static RelationCollection getFromFuture(final Future<RelationCollection> future) {
try {
return future.get();
} catch (final ExecutionException e) {
e.printStackTrace();
} catch (final InterruptedException e) {
e.printStackTrace();
}
return null;
}
public void remove(final Integer id) {
cache.remove(id);
}
}
我删除了clean方法,因为在很多情况下它都不起作用。对象不会总是转换为软引用。对象可以从强引用直接转换为幻影引用,因此不会出现在引用队列中。我将清理工作作为load and get操作的一部分嵌入其中。