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()set()、 whileの同期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()キャッシュとデコレータ インスタンス間の 1 対 1 のマッピングが保証されるので、これで問題ないと思います。
4

2 に答える 2

1
  • このコードはスレッドセーフですか?

はい。装飾された SimpleCache のインスタンスが渡されないと仮定します。

  • 同期されているクラスの外で ReadWriteLock を宣言するのは悪い習慣ですか? SimpleCache.getDecorator() は、キャッシュとデコレータ インスタンス間の 1 対 1 のマッピングを保証するので、これで問題ないと思います。

いいえ。コメントで説明されているように、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 に答える