您的位置:首页 > 其它

Guava Cache特性:对于同一个key,只让一个请求回源load数据,其他线程阻塞等待结果

2016-12-06 19:48 776 查看
设想高并发下的一种场景:假设我们将name=aty存放到缓存中,并设置的有过期时间。当缓存过期后,恰好有10个客户端发起请求,需要读取name的值。使用Guava Cache可以保证只让一个线程去加载数据(比如从数据库中),而其他线程则等待这个线程的返回结果。这样就能避免大量用户请求穿透缓存。

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

import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Main {

// 1s无访问则缓存过期, 每次加载一个key需要耗时2s
private static LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.SECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
System.out.println("begin to query db...");
Thread.sleep(2000);
System.out.println("success to query db...");
return UUID.randomUUID().toString();
}
});

private static CountDownLatch latch = new CountDownLatch(1);

public static void main(String[] args) throws Exception {

cache.put("name", "aty");
Thread.sleep(1500);

for (int i = 0; i < 8; i++) {
startThread(i);
}

// 让线程运行
latch.countDown();

}

private static void startThread(int id) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "...begin");
latch.await();
Stopwatch watch = Stopwatch.createStarted();
System.out.println("value..." + cache.get("name"));
watch.stop();

System.out.println(Thread.currentThread().getName() + "...finish,cost time=" + watch.elapsed(TimeUnit.SECONDS));
} catch (Exception e) {
e.printStackTrace();
}
}
});

t.setName("Thread-" + id);
t.start();
}

}



上面输出结果可以看到:只有一个线程去数据库中加载数据,其他线程都在等待(每个线程都耗时2s)。使用Guava确实可以做到:对于同一个key,无论有多少请求,都只会允许一个线程去加载数据。

但是也有一个很致命的缺陷:上面8个线程中,有一个线程实际去加载数据,其余7个线程都被阻塞了。如果能做到,当一个线程去加载数据,其余线程发现这个数据正在加载中,那么直接读取老的数据,这样就不会阻塞了。既然是缓存,读取旧一点数据也没有多大问题,却可以提高系统吞度量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐