在“Java Concurrency in Practice”中将SoftReferences添加到高效且可伸缩的结果缓存中

时间:2010-01-31 20:05:38

标签: java multithreading concurrency

我一直在阅读Java Concurrency in Practice一书。它绝对是一个很好的参考。我正在尝试扩展高效且可伸缩的结果集缓存的最后一个示例,以包含软引用。存储在我的缓存中的对象可能会变得相当大,我需要一些方法让这些对象在内存不足时收集垃圾,从而获得软引用。让线程安全和软的参考概念起作用已经测试了我的极限,我需要帮助。到目前为止,这是我的工作。我不确定这是否真的是线程安全的。有谁可以看一看并发表评论?

public class RelationCollectionCache {

    // properties

    private final ConcurrentMap<Integer, Reference<Future<RelationCollection>>> cache =
        new ConcurrentHashMap<Integer, Reference<Future<RelationCollection>>>();

    private final ReferenceQueue<? super Future<RelationCollection>> queue = 
        new ReferenceQueue<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();

        if (future == null) {

            Callable<RelationCollection> eval = new Callable<RelationCollection>() {
                public RelationCollection call() throws Exception {
                    return compute(id);
                }
            };

            FutureTask<RelationCollection> task = new FutureTask<RelationCollection>(eval);
            reference = cache.putIfAbsent(id, new InternalSoftReference(id, task));
            if (((reference == null) ? null : reference.get()) == null) {
                future = task;
                task.run();
            }

        }

        try {
            return future.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;

    }

    private RelationCollection compute(Integer id) {

        RelationCollection collection = new RelationCollection();

        // lengthy computation of a large collection

        return collection;

    }

    public RelationCollection get(Integer id) {

        clean();

        Reference<Future<RelationCollection>> reference = cache.get(id);
        Future<RelationCollection> future = (reference == null) ? null : reference.get();
        if (future == null)
            return null;

        try {
            return future.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;

    }

    public void remove(Integer id) {

        clean();

        cache.remove(id);

    }

    public void clean() {

        InternalSoftReference reference = (InternalSoftReference) queue.poll();
        while (reference != null) {
            cache.remove(reference.id);
            reference = (InternalSoftReference) queue.poll();
        }

    }

    // internal classes

    private class InternalSoftReference extends SoftReference<Future<RelationCollection>> {

        private Integer id;

        public InternalSoftReference(Integer id, Future<RelationCollection> future) {
            super(future, queue);
            this.id = id;
        }

    }

}

此实现在每个操作中调用clean方法以清除垃圾收集的引用。这也可能是一个线程,但它包含了我甚至没有探索过的另一个并发级别。

2 个答案:

答案 0 :(得分:3)

这非常困难。您可以使用MapMaker中的google-collections吗?

答案 1 :(得分:1)

这是我的传球。我没有测试过这段代码,但我认为它可以捕获大部分案例

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

我删除了干净的方法,因为在很多情况下它不起作用。对象不会总是转换为轻柔引用。对象可以直接强烈引用引用的幻像,因此不会出现在引用队列中。我将清理作为加载的一部分嵌入并进行操作。