1

我们正在我们的应用程序中构建由内存、文件和远程服务支持的缓存存储。希望避免显式同步以保持存储简单,同时使用装饰器解决阻塞等行为问题。

这是一个简单的缓存,这只是一个例子!

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()返回一个为and提供同步的装饰器,set()同时允许装饰器在不知道为什么或如何刷​​新的情况下检查是否应该刷新键。我从这里得到了一个同步装饰器的想法。isKeyStale()refreshKey()

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()确保缓存和装饰器实例之间的一对一映射,所以我假设这是可以的。
4

2 回答 2

1
  • 这段代码是线程安全的吗?

是的。假设修饰的 SimpleCache 的实例没有被传递。

  • 在同步的类之外声明 ReadWriteLock 是不好的做法吗?SimpleCache.getDecorator() 确保缓存和装饰器实例之间的一对一映射,所以我假设这没问题。

不。虽然同样值得注意的是,正如评论中所讨论的,BlockingCacheDecorator 通常会实现一个 Cache 接口。

于 2014-07-04T20:05:47.060 回答
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实例的意图是明确的。)

注意:您还可以使用匿名内部类(或可能是闭包)来提供工厂实现。

于 2014-07-04T20:07:54.987 回答