2016-09-03 3 views
1

구아바 캐시에 문제가 있습니다. 캐시에 하나의 요소 만있는 경우 문제가 없습니다. 나는 두 번째 요소를로드 할 때, 그것의 로그는 말한다구버 키를 고려한 구아바 캐시

private static LoadingCache<String, MyClass> cache = null; 
.... 
public MyClass method(final String id1, final long id2) { 
    log.error("inside with "+id1); 
    final String cacheKey = id1+"-"+id2; 
    if(cache == null){ 
     cache = CacheBuilder.newBuilder() 
     .maximumSize(1000) 
     .build(
      new CacheLoader<String, MyClass>() { 
       @Override 
       public MyClass load(String key) { 
        return getValue(cacheKey); 
       } 
      } 
     ); 
    } 
    try { 
     return cache.get(cacheKey); 
    } catch (ExecutionException ex) { 
     log.error("EEE missing entry",ex); 
    } 
} 

private MyClass getValue(String cacheKey){ 
    log.error("not from cache "+cacheKey); 
    ... 

} 

이전 항목의 키를 선택하려고 : 내가 호출 할 때, 예를 들어

inside with 129890038707408035563943963861595603358 
not from cache 1663659699-315839912047403113610285801857400882820 // This is key for the earlier entry 

방법 ("1", 2), 캐시에 값을로드하고 이후에 캐시에서 값을 가져올 수 있습니다. 이제 메서드 ("3", 4)를 호출합니다. 캐시에 없으므로 getValue()가 호출되고 메서드 ("1", 2)의 키가 출력됩니다.

어디서 잘못 되었나요?

+0

어떤 키? 재현하기위한 모든 단계를 제공하십시오. –

+0

캐시를 초기화하는 방법은 스레드로부터 안전하지 않습니다. 또한 매우 정적 인 방법을 기반으로하는 CacheLoader를 사용하는 정적 캐시이기 때문에 오류가 발생하기 쉽습니다. 나는 당신의 실수가 그 중 하나 때문일 것이라고 생각한다. –

+0

이 규칙 (http://stackoverflow.com/help/mcve)을 읽어주십시오. –

답변

3

문제는 사용자가 CacheLoader을 만드는 방법과 관련이 있습니다. 잘 확인하면 해당 캐시 키 (캐시가 지연 초기화 될 때 로컬 변수 cacheKey의 값)로 초기화한다는 것을 확인할 수 있습니다. 더 일반적이고 key에 제공된 매개 변수로 loadCacheLoader으로 지정해야합니다. 그렇지 않으면 getValue(key)을 동일한 키로 호출하여 캐시를로드합니다.

그것은이 있어야한다 :

new CacheLoader<String, MyClass>() { 
    @Override 
    public MyClass load(String key) { 
     return getValue(key); // instead of return getValue(cacheKey); 
    } 
} 

NB : 당신이 당신의 캐시가 초기화되지 않았습니다 당신의 방법 method가 호출되어 참으로 경우, 안전스레드되지 초기화하는 방법 여러 개의 스레드가 동시에 하나가 아닌 여러 번 생성됩니다.

private static volatile LoadingCache<String, MyClass> cache = null; 
public MyClass method(final String id1, final long id2) { 
    ... 
    if(cache == null){ 
     synchronized (MyClass.class) { 
      if(cache == null){ 
       cache = ... 
      } 
     } 
    } 

NB :비 정적을 기반으로 CacheLoader정적 캐시를 초기화하지 마십시오

한 가지 방법은 다음 이중 확인 잠금 관용구를 사용하여 수 메서드를 사용하면 너무 오류가 발생하기 쉽습니다. 둘 다 정적이 아닌 또는 static으로 지정하고 혼합하지 마십시오.

당신이 모두정적, 캐시 초기화가 매우 간단하게 될 것입니다 수 있다고 가정하면, 단순히 다음과 같습니다

private static final LoadingCache<String, MyClass> cache = CacheBuilder.newBuilder()... 

필요 없음을 유유히 초기화하는 것이 많게의 코드를 단순화 할 당신의 방법으로 간단하게 줄일 수 있습니다 :

public MyClass method(final String id1, final long id2) { 
    log.error("inside with "+id1); 
    final String cacheKey = id1+"-"+id2; 
    try { 
     return cache.get(cacheKey); 
    } catch (ExecutionException ex) { 
     log.error("EEE missing entry",ex); 
    } 
} 
+0

충분히 명확한가요? 내가 제안한 것을 테스트 해 봤니? –

+2

또한'Suppliers.memoize'가 이중 체크 잠금 이디엄을 캡슐화하도록 고려하십시오 –

+0

지금 만 테스트 할 수 있습니다, 고맙습니다. – rajesh

관련 문제