2017-04-21 13 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来确保发生之前的范式,所以其他线程会立即看到缓存是否已被填充;
  • 新的缓存值创建必须同步,因此每个并发的 线程不会生成它自己的值,在竞争条件期间覆盖以前的值;
  • 同步是非常昂贵的,所以我们只需要在需要的时候使用它,并且由于相同的竞争条件,您可能会同时获得多个线程监视器。这就是为什么需要在同步块之后进行另一次检查,以确保其他线程尚未填充该值。
相关问题