2017-04-21 19 views
-2

Iいる(失敗ユニットテストと一緒に)クラスの以下のセット:HashMapを使用してキャッシュから同じキーに同じオブジェクトを返す方法

スプロケット:

public class Sprocket { 
    private int serialNumber; 

    public Sprocket(int serialNumber) { 
     this.serialNumber = serialNumber; 
    } 

    @Override 
    public String toString() { 
     return "sprocket number " + serialNumber; 
    } 
} 

SlowSprocketFactory:

public class SlowSprocketFactory { 
    private final AtomicInteger maxSerialNumber = new AtomicInteger(); 

    public Sprocket createSprocket() { 
     // clang, click, whistle, pop and other expensive onomatopoeic operations 
     int serialNumber = maxSerialNumber.incrementAndGet(); 
     return new Sprocket(serialNumber); 
    } 

    public int getMaxSerialNumber() { 
     return maxSerialNumber.get(); 
    } 
} 

SprocketCache:

public class SprocketCache { 

    private SlowSprocketFactory sprocketFactory; 
    private Sprocket sprocket; 

    public SprocketCache(SlowSprocketFactory sprocketFactory) { 
     this.sprocketFactory = sprocketFactory; 
    } 

    public Sprocket get(Object key) { 
     if (sprocket == null) { 
      sprocket = sprocketFactory.createSprocket(); 
     } 

     return sprocket; 
    } 
} 

TestSprocketCacheユニットテスト:

public class TestSprocketCache { 

    private SlowSprocketFactory sprocketFactory = new SlowSprocketFactory(); 

    @Test 
    public void testCacheReturnsASprocket() { 
     SprocketCache cache = new SprocketCache(sprocketFactory); 
     Sprocket sprocket = cache.get("key"); 
     assertNotNull(sprocket); 
    } 

    @Test 
    public void testCacheReturnsSameObjectForSameKey() { 
     SprocketCache cache = new SprocketCache(sprocketFactory); 

     Sprocket sprocket1 = cache.get("key"); 
     Sprocket sprocket2 = cache.get("key"); 

     assertEquals("cache should return the same object for the same key", sprocket1, sprocket2); 
     assertEquals("factory's create method should be called once only", 1, sprocketFactory.getMaxSerialNumber()); 
    } 
} 

TestSprocketCacheユニットテストは常に次のように私は次のように変更しても緑色のバーを返します。

Sprocket sprocket1 = cache.get("key"); 
Sprocket sprocket2 = cache.get("pizza"); 

は(私がSprocketCache.get内HashMap.contains(キー)を使用する必要があること推測しています)メソッドを使用していますが、ロジックを理解できないようです。

+0

あなたは* HashMapを使用していません。それは*問題です*あなたが何を求めているのか不明です。 – EJP

答えて

0

あなたがここに抱えている問題は、あなたのget(Object)実装はインスタンスが1つだけ作成することを可能にするということです。

public Sprocket get(Object key) { 
     // Creates object if it doesn't exist yet 
     if (sprocket == null) { 
      sprocket = sprocketFactory.createSprocket(); 
     } 

     return sprocket; 
    } 

これは典型的な遅延ロードインスタンス化シングルトンパターンです。 getを再度呼び出すと、インスタンスはsprocketに割り当てられ、インスタンシエーションは完全にスキップされます。 keyパラメータはまったく使用しないので、何も影響を与えません。これも不思議はそれが常に同じキャッシュされたワンス値を返さない、

public class SprocketCache { 

    private SlowSprocketFactory sprocketFactory; 
    private Map<Object, Sprocket> instances = new HashMap<Object, Sprocket>(); 

    public SprocketCache(SlowSprocketFactory sprocketFactory) { 
     this.sprocketFactory = sprocketFactory; 
    } 

    public Sprocket get(Object key) { 
     if (!instances.containsKey(key)) { 
      instances.put(sprocket); 
     } 

     return instances.get(key); 
    } 
} 
0

さて、あなたの現在のキャッシュの実装では、キーに依存しない:Mapを使用して

は確かにあなたの目的を達成するための一つの方法だろう。

public class SprocketCache { 

    private SlowSprocketFactory sprocketFactory; 
    private ConcurrentHashMap<Object, Sprocket> cache = new ConcurrentHashMap<?>(); 

    public SprocketCache(SlowSprocketFactory sprocketFactory) { 
     this.sprocketFactory = sprocketFactory; 
    } 

    public Sprocket get(Object key) { 
     if (!cache.contains(key)) { 
      // we only wan't acquire lock for cache seed operation rather than for every get 
      synchronized (key){      
       // kind of double check locking to make sure no other thread has populated cache while we were waiting for monitor to be released 
       if (!cache.contains(key)){ 
        cache.putIfAbsent(key, sprocketFactory.createSprocket()); 
       } 
      } 
     } 
     return cache.get(key); 
    } 
} 

カップルの重要な側面ノート:

    あなたがキーに異なる値を格納したい、とあなたはそれがスレッドセーフにしたいと仮定した場合

    は、あなたはこのような何かをやって終わるかもしれません
  • あなたはCocncurrentHashMapが起こる前のパラダイムを保証する必要があります。そうすれば、他のスレッドはキャッシュがいっぱいになったかどうかを直ちに見ることができます。
  • 新しいキャッシュ値の作成を同期させる必要があるため、スレッドごとに独自の値を生成せず、競合状態で以前の値を上書きします。
  • 同期が非常に高価なので、必要なときにはそれに従わないようにしてください。同じ競合状態のため、同時に複数のスレッドがモニタを保持することがあります。そのため、他のスレッドがその値をまだ満たしていないことを確認するために、synchronizedブロックの後に別のチェックが必要です。
関連する問題