ReadWriteLock装饰器,这个代码线程安全吗?

时间:2014-07-04 19:00:20

标签: java multithreading synchronization blocking reentrantreadwritelock

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

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

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

2 个答案:

答案 0 :(得分:1)

  • 此代码是否是线程安全的?

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

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

没有。虽然值得注意的是,正如评论中所讨论的,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个实例是明确的。)

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