我们在由内存,文件和远程服务支持的应用程序中构建缓存存储。想要避免显式同步以保持商店简单,同时使用装饰器来解决阻塞等行为问题。
这是一个简单的缓存,这只是一个例子!
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()
确保缓存和装饰器实例之间的一对一映射,所以我假设这没问题。答案 0 :(得分:1)
是。假设没有传递装饰的SimpleCache的实例。
没有。虽然值得注意的是,正如评论中所讨论的,BlockingCacheDecorator通常会实现Cache接口。
答案 1 :(得分:1)
在目前的形式中,代码非常简单,因为没有什么能阻止调用者直接调用SimpleCache
的方法,或者确实将同一个SimpleCache
实例传递给多个装饰器,导致更多混乱。
如果您承诺永远不会这样做,那么它在技术上是线程安全的,但我们都知道这些承诺的价值是多少。
如果目标是能够使用底层缓存的不同实现,我将创建一个CacheFactory
接口:
interface CacheFactory {
Cache newCache();
}
工厂的示例实现:
class SimpleCacheFactory implements CacheFactory {
private final String cacheName; //example cache parameter
public SimpleCacheFactory( String cacheName ) {
this.cacheName = cacheName;
}
public Cache newCache() {
return new SimpleCache( cacheName );
}
}
最后你的代表班:
public class BlockingCacheDecorator {
private final Cache delegate;
private final ReentrantReadWriteLock lock;
public BlockingCacheDecorator(CacheFactory factory){
delegate = factory.newCache();
lock = new ReentrantReadWriteLock();
}
//rest of the code stays the same
}
这样可以更加有力地保证您的Cache
实例不会被外部代理无意中重复使用或访问。 (也就是说,除非工厂被故意误解,但至少你不打算重用Cache
个实例是明确的。)
注意:您还可以使用匿名内部类(或可能是闭包)来提供工厂实现。