Guava cachebuilder - 在加载进行时放置

时间:2013-11-08 14:31:52

标签: java caching guava

考虑这个简单的程序:

import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

public class Main {

    final static Logger logger = Logger.getLogger(Main.class.getName());

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        final LoadingCache<Integer, String> cache = CacheBuilder.newBuilder().build(
                new CacheLoader<Integer, String>() {

                    @Override
                    public String load(Integer arg0) throws Exception {
                        logger.info("Cache builder START: " + arg0);
                        Thread.sleep(4000);
                        logger.info("Cache builder FINISH: " + arg0);
                        return "This is what CacheBuilder returned for key " + arg0;
                    }
                });

        Thread getterThread = new Getter(cache);
        getterThread.start();

        Thread setterThread = new Setter(cache);
        setterThread.start();

        getterThread.join();
        setterThread.join();

        logger.info("Finally in cache we have: " + cache.get(1));

    }

    private static final class Getter extends Thread {
        private final LoadingCache<Integer, String> cache;

        private Getter(LoadingCache<Integer, String> cache) {
            this.cache = cache;
        }

        @Override
        public void run() {
            try {
                logger.info("Getter thread reads 1st time " + cache.get(1)
                        + "  <<<<<<<<<< WHAT !?!");
                // allow the setter to put the value
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                logger.info("Getter thread reads 2nd time " + cache.get(1));
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    private static final class Setter extends Thread {
        private final LoadingCache<Integer, String> cache;

        private Setter(LoadingCache<Integer, String> cache) {
            this.cache = cache;
        }

        @Override
        public void run() {
            try {
                // deliberately wait to allow the Getter thread
                // trigger cache loading
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                cache.put(1, "This isn't where I parked my car!");
                logger.info("Setter thread now reads: " + cache.get(1));
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

输出是:

2013-11-08 15:24:32 INFO Main$1 load Cache builder START: 1
2013-11-08 15:24:32 INFO Main$Setter run Setter thread now reads: This isn't where I parked my car!
2013-11-08 15:24:36 INFO Main$1 load Cache builder FINISH: 1
2013-11-08 15:24:36 INFO Main$Getter run Getter thread reads 1st time This is what CacheBuilder returned for key 1  <<<<<<<<<< WHAT !?!
2013-11-08 15:24:37 INFO Main$Getter run Getter thread reads 2nd time This isn't where I parked my car!
2013-11-08 15:24:37 INFO Main main Finally in cache we have: This isn't where I parked my car!

我在Getter线程中得到了“这就是CacheBuilder为key 1返回的内容”。 显然这是因为Getter调用的get(1)触发了缓存加载,但同时Setter线程会为key 1添加一些其他值。 我希望它能像Setter那样返回原来的东西=“这不是我停车的地方!” (我第二次得到Getter检索1的值)。

我错过了什么吗?

提前致谢

1 个答案:

答案 0 :(得分:0)

是。缓存的内部数据结构是同步的,以保护您免受这种污染。你头脑中的模型应该是:只要一个线程正在使用缓存,它就有自己的副本。

因此第一个线程触发缓存加载。番石榴“克隆”缓存(在内部,它只是确保没有人可以改变线程1看到的结构)。 4秒后,线程获取缓存加载返回的结果,无论有多少其他线程在平均时间内更改了值(它们都有自己的“副本”进行修改)。

当线程1完成时,缓存会自行更新。现在,线程2的变化对于线程1变得可见。