代码之家  ›  专栏  ›  技术社区  ›  Ricardo Marimon

在“实践中的Java并发”中向高效、可扩展的结果缓存添加软引用

  •  4
  • Ricardo Marimon  · 技术社区  · 15 年前

    我已经阅读了Java并发性练习本。这绝对是一个很好的参考。我试图扩展最后一个高效、可伸缩的结果集缓存示例,以合并软引用。存储在缓存中的对象可能会变得相当大,我需要一些方法,以便在内存不足时对这些对象进行垃圾收集,因此需要软引用。让线程安全和软参考概念发挥作用已经考验了我的极限,我需要帮助。到目前为止,这就是我的工作。我不确定这是否真的是线程安全的。有人能看一下并发表评论吗?

    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 回复  |  直到 15 年前
        1
  •  4
  •   Kevin Bourrillion Gergely    15 年前

    这非常困难。你能用它吗 MapMaker 从…起 google-collections ?

        2
  •  1
  •   Michael Barker    15 年前

    这是我的通行证。我还没有测试过这段代码,但我认为它能捕获大多数情况

    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操作的一部分嵌入其中。