我正在开发一个需要实现缓存服务来提供请求的java应用程序。要求如下:
1)一个或多个线程来获取一些数据,如果数据为null则是 然后只有一个线程进入DB以在缓存中加载数据。
2)完成后,所有后续线程都将从缓存中提供。
因此,实现如下:
public List<Tag> getCachedTags() throws Exception
{
// get data from cache
List<Tag> tags = (List<Tag>) CacheUtil.get(Config.tagCache,Config.tagCacheKey);
if(tags == null) // if data is null
{
// one thread will go to DB and others wait here
synchronized(Config.tagCacheLock)
{
// first thread get this null and go to db, subsequent threads returns from here.
tags = (List<Tag>) CacheUtil.get(Config.tagCache,Config.tagCacheKey);
if(tags == null)
{
tags = iTagService.getTags(null);
CacheUtil.put(Config.tagCache, Config.tagCacheKey, tags);
}
}
}
return tags;
}
现在这是正确的方法,因为我在一个静态字符串中锁定,那么它不是一个类级锁吗?请建议我一些更好的方法
答案 0 :(得分:2)
如果要全局同步,只需使用自定义对象:
private static final Object lock = new Object();
不要使用String
常量作为实习,因此在程序的完全不同部分声明的具有相同内容的字符串常量将是相同的String
对象。并且通常避免锁定静态字段。最好实例化您的类并将锁声明为非静态。目前你可以将它用作单例(使用某些方法,如Cache.getInstance()
),但后来当你意识到你必须支持几个独立的缓存时,你需要更少的重构才能实现这一点。
在Java-8中,获取对象的首选方法是使用ConcurrentHashMap.computeIfAbsent
,如下所示:
private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
public List<Tag> getCachedTags() throws Exception
List<Tag> tags = (List<Tag>)cache.computeIfAbsent(Config.tagCacheKey,
k -> iTagService.getTags(null));
return tags;
}
这很简单而且很健壮。在以前的Java版本中,您可能使用AtomicReference
来包装对象:
private final ConcurrentHashMap<String, AtomicReference<Object>> cache =
new ConcurrentHashMap<>();
public List<Tag> getCachedTags() throws Exception
AtomicReference<Object> ref = cache.get(key);
if(ref == null) {
ref = new AtomicReference<>();
AtomicReference<Object> oldRef = cache.putIfAbsent(key, ref);
if(oldRef != null) {
ref = oldRef;
}
synchronized(ref) {
if(ref.get() == null) {
ref.set(iTagService.getTags(null));
}
}
}
return (List<Tag>)ref.get();
}