2017-04-21 67 views
-2

我有以下組類(具有失敗的單元測試一起):如何使用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)實現只允許創建一個實例:

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

     return sprocket; 
    } 

這是一個典型的懶加載實例模式。如果再次調用get,則實例將被分配到sprocket,並且它將完全跳過實例化。請注意,根本不使用key參數,所以它不會影響任何內容。

使用Map確實是一個方式來實現你的目標:

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

好吧,你目前的緩存實現不依賴於密鑰,所以難怪它總是返回相同的緩存一次值。

如果你想存儲的密鑰不同的值,並假設你希望它是線程安全的,你可能最終做這樣的事情:

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來確保發生之前的範式,所以其他線程會立即看到緩存是否已被填充;
  • 新的緩存值創建必須同步,因此每個併發的 線程不會生成它自己的值,在競爭條件期間覆蓋以前的值;
  • 同步是非常昂貴的,所以我們只需要在需要的時候使用它,並且由於相同的競爭條件,您可能會同時獲得多個線程監視器。這就是爲什麼需要在同步塊之後進行另一次檢查,以確保其他線程尚未填充該值。
相關問題