【发布时间】:2014-07-04 19:00:19
【问题描述】:
我们正在由内存、文件和远程服务支持的应用中构建缓存存储。希望避免显式同步以保持商店简单,同时使用装饰器解决阻塞等行为问题。
这里是一个简单的缓存,这只是一个例子!
import java.util.HashMap;
public class SimpleCache {
private HashMap<String,Object> store;
private final BlockingCacheDecorator decorator;
public SimpleCache(){
store = new HashMap<String,Object>();
decorator = new BlockingCacheDecorator(this);
}
//is NOT called directly, always uses decorator
public Object get(String key){
return store.get(key);
}
//is NOT called directly, always uses decorator
public void set(String key, Object value){
store.put(key, value);
}
//is NOT called directly, always uses decorator
public boolean isKeyStale(String key){
return !(store.containsKey(key));
}
//is NOT called directly, always uses decorator
public void refreshKey(String key){
store.put(key, new Object());
}
public BlockingCacheDecorator getDecorator(){
return decorator;
}
}
getDecorator() 返回一个为get() 和set() 提供同步的装饰器,而isKeyStale() 和refreshKey() 允许装饰器在不知道为什么或如何刷新的情况下检查是否应该刷新键。我从here 得到了一个同步装饰器的想法。
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class BlockingCacheDecorator {
private SimpleCache delegate;
private final ReentrantReadWriteLock lock;
public BlockingCacheDecorator(SimpleCache cache){
delegate = cache;
lock = new ReentrantReadWriteLock();
}
public Object get(String key){
validateKey(key);
lockForReading();
try{
return delegate.get(key);
}finally{ readUnlocked(); }
}
public void setKey(String key, Object value){
lockForWriting();
try{
delegate.set(key,value);
}finally{ writeUnlocked(); }
}
protected void validateKey(String key){
if(delegate.isKeyStale(key)){
try{
lockForWriting();
if(delegate.isKeyStale(key))
delegate.refreshKey(key);
}finally{ writeUnlocked(); }
}
}
protected void lockForReading(){
lock.readLock().lock();
}
protected void readUnlocked(){
lock.readLock().unlock();
}
protected void lockForWriting(){
lock.writeLock().lock();
}
protected void writeUnlocked(){
lock.writeLock().unlock();
}
}
问题:
- 假设
SimpleCache仅通过其装饰器使用,代码是否是线程安全的? - 在同步的类之外声明
ReadWriteLock是不好的做法吗?SimpleCache.getDecorator()确保缓存和装饰器实例之间的一对一映射,所以我假设这没问题。
【问题讨论】:
-
缓存通常用在并发环境中,所以互斥对它们来说不是什么切线的东西。这个
Cache被单线程使用没有什么意义。那你为什么要创建Cache方法public?恕我直言,将Cache作为接口更清楚,然后有一个默认情况下是线程安全的实现。那么,你看过番石榴CacheBuilder吗? -
鉴于任何人都可以调用
SimpleCache类的未受保护的get()和set()方法,答案是肯定的。我不得不承认,虽然我真的不明白为什么缓存的最内在特性(即它为多个线程存储和检索信息)首先需要一个装饰器。 -
我以前用过这种形式的装饰器模式来做缓存,它对于混合和匹配不同的缓存特性非常强大。大多数库都有用于打开和关闭功能的标志,然后有大量的 if 检查哪些混淆了水。通过组合创建缓存策略非常好用。但它可能并不适合每一个人。
-
组合很好,但应该以防止未同步实例“泄漏”的方式完成。 (许多其他功能也可能依赖于此,例如实现单独的大小上限。)
-
@biziclop 装饰器允许将行为问题外部化和重用,例如,我们有
BlockingDecorator、ReadOnlyDecorator、ReadWriteDecorator等,它使缓存代码更加干净。我知道 SimpleCache 上的get()/set()是完全开放的,在实际实现中它们使用闭包进行保护,我的错,我应该在前面提到。
标签: java multithreading synchronization blocking reentrantreadwritelock