caffeine的load put 和invalidate操作都是原子的,这个意思是这3个操作是互斥的,load和put是不能同时执行的,load和invalidate也是不能同时执行的。
先load再invalidate,invalidate操作是要等load操作执行完的。如果load操作执行比较慢,那invalidate操作就要等很久了。
这样做有什么好处呢,可以保证结果完全符合预期。
这种方式和guava是不同的,guava是不阻塞的。
先load再invalidate,invalidate操作是马上执行完的,不需要等待load这种耗时操作执行完。这个其实不合预期的,根本达不到invalidate的效果了。
先load再invalidate,本意是要让当前的load操作失效,但是load操作时间比较长,load操作结束是在invalidate之后了,失效不了这吃load操作。
说概念可能比较抽象,我们来举个例子:
public static void main(String[] args) throws Exception {
final AtomicInteger atomicInteger = new AtomicInteger();
final LoadingCache<String, String> cache = CacheBuilder.newBuilder().maximumSize(500)
.build(new CacheLoader<String, String>() {
@Override
public String load(String s) throws Exception {
Thread.sleep(1000);
return atomicInteger.incrementAndGet() + "";
}
});
cache.get("test");
cache.invalidate("test");
new Thread() {
@Override
public void run() {
try {
String value = cache.get("test");
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
cache.invalidate("test");
System.out.println("use ms:" + (System.currentTimeMillis() - start));
}
}.start();
Thread.sleep(1200);
System.out.println("========" + cache.asMap());
System.out.println("========" + cache.get("test"));
}
结果是:
use ms:0
========{test=2}
========2
其实我们期望的结果是3的,不能满足我们的期望。
再看看caffeine,这个时候原子性就的好处就得到体现了。
public static void main(String[] args) throws Exception {
AtomicInteger atomicInteger=new AtomicInteger();
LoadingCache<String, String> cache = Caffeine.newBuilder().maximumSize(3).build(key -> {
Thread.sleep(1000);
return atomicInteger.incrementAndGet()+"";
});
cache.get("test");
cache.invalidate("test");
new Thread() {
@Override
public void run() {
cache.get("test");
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
cache.invalidate("test");
System.out.println("use ms:"+(System.currentTimeMillis() - start));
}
}.start();
Thread.sleep(1200);
System.out.println("========" + cache.asMap());
System.out.println("========" + cache.get("test"));
}
结果是:
use ms:802
========{}
========3
这个就是我们想要的结果了,虽然invalidate阻塞了一段时间。
caffeine实现原理
其实原理很简单,caffeine的存储就是ConcurrentHashMap,利用了ConcurrentHashMap自己的node节点锁。
invalidate操作对应的就是remove方法
image.png可以看到remove是加锁的
而load方法对应的是compute方法
image.pngremappingFunction.apply 这里对应的就是我们具体的load的方法内容
网友评论