2013-03-19 53 views
0

我們使用HashMap來緩存方法上註釋的查找。註釋可以通過Spring的AnnotationUtils.findAnnotation進行檢索。不使用緩存會導致嚴重的性能下降。緩存註釋信息的地圖是否需要同步?

我們的實現看起來莫名其妙地想:

public class SomeService { 

    // Caches annotations on methods. The value can be null! 
    private static final Map<Method, MyAnnotation> ANNOTATION_CACHE = new HashMap<Method, MyAnnotation>(); 

    private MyAnnotation findAnnotation(Method m) { 
     if (ANNOTATION_CACHE.containsKey(m)) { 
      return ANNOTATION_CACHE.get(m); 
     } 

     MyAnnotation a = AnnotationUtils.findAnnotation(m, MyAnnotation.class); 
     ANNOTATION_CACHE.put(m, a); 

     return a; 
    } 

    public void doSomethingWith(Class<?> clazz) { 
     for (Method m : clazz.getMethods()) { 
      MyAnnotation a = findAnnotation(m); 
      if (a != null) { 
       // do something with annotation a 
      } 
     } 
    } 
} 

的,現在的問題是,如果我需要在訪問ANNOTATION_CACHE地圖或不同步。可能發生的最糟糕的事情是,兩個並行線程將相同的(m,a)對放入緩存映射中,這不會造成傷害,不是嗎?

我的第一個想法是使用ConcurrentHashMap,但它不允許空值(如果一個方法沒有註解=> null,這裏是需要的)。使用Collections.synchronizedMap()並同步每個對地圖的訪問也並不理想,因爲doSomethingWith()方法被非常頻繁地調用。

那麼在這種情況下,是否真的有必要同步訪問HashMap?緩存nevery在運行時更改,鍵/值對只能插入一次,永遠不會被刪除,但會被多次讀取。

有什麼想法?

+0

除了同步(並請在那裏採取@noamt的建議),您可以通過存儲「列表」來解決「無空」限制。如果你最終需要'doSomething()'處理多種註解類型,這也將簡化你的生活。 – parsifal 2013-03-19 15:22:10

+0

我也有這樣的假設:「可能發生的最糟糕的事情是兩個線程並行地將相同的(m,a)對放入緩存映射中,這不會造成傷害,不是嗎?」 - 這是錯誤的。細節在這個優秀的博客文章http://mailinator.blogspot.no/2009/06/beautiful-race-condition.html – hennings 2014-07-03 10:06:16

回答

3

如果您有一個專門寫入地圖的階段,然後是專門從中讀取地圖的另一個階段,那麼您不需要併發收集。 你也可以通過用不可變的地圖寫後階段封裝地圖來確保地圖保持不變。

例如,利用番石榴的Immutable map

ImmutableMap.copyOf(map); 


如果你預見到並行讀/寫/刪除訪問的集合,那麼你絕對應該使用的ConcurrentHashMap;因爲讀/寫/刪除操作不是原子的,你可能會得到一些非常奇怪的結果。


我首先想到的是使用的ConcurrentHashMap,但它不會允許空值(這是這裏所需要如果一個方法沒有註解=>空)

然後首先不要插入空值;更好的是,從地圖上刪除現有的密鑰。

0

現在的問題是,如果我需要同步訪問 ANNOTATION_CACHE地圖與否。可能發生的最糟糕的事情是 兩個線程並行地將相同的(m,a)對放入緩存映射中, 這不會傷害,不是嗎?

是,如果第一次就把需要擴展陣列支持HashMap的

如果第二個看跌時調用了第一次放進擴展HashMap的,壞的和不可預知的結果可以happend,可以傷害。