2012-09-14 4 views
3

우리는 ConcurrentHashMap에 의해 구현되는 간단하면서도 매우 많이 사용되는 캐시를 가지고 있습니다. 이제 모든 값을 정기적으로 (예 : 매 15 분마다) 새로 고침하고 싶습니다.java 캐시의 시간 초과

나는이 같은 코드 싶습니다

private void regularCacheCleanup() { 
     final long now = System.currentTimeMillis(); 
     final long delta = now - cacheCleanupLastTime; 
     if (delta < 0 || delta > 15 * 60 * 1000) { 
      cacheCleanupLastTime = now; 
      clearCache(); 
     } 
    } 

를이해야 제외 :

  • 스레드 안전
  • 비 차단 및 캐시 삭제하지 않을 경우 매우 성능이 좋은
  • 자바. * 클래스를 제외하고는 아무런 종속성이 없습니다. (따라서 Google CacheBuilder는 없습니다.)
  • 견고한 ;-)
  • 은 지금 나는의 ThreadLocal의 짧은 타이머를 구현하는 생각 새로운 스레드

을 시작할 수 없습니다. 이 시간이 만료되면 실시간 타이머가 동기화 방식으로 검사됩니다. 그러나 엄청난 양의 코드가 있기 때문에 더 간단한 아이디어가 좋을 것입니다.

+1

EHCache와 같은 라이브러리를 사용하지 않는 이유는 무엇입니까? – beny23

+0

EHCache를 사용하여 동일한 @ benny23을 제안합니다. XML 파일에서 새로 고침 시간을 구성하는 것만 큼 간단합니다. 프로그래밍 방식으로하지 않을 경우 – jmventar

+0

이것은 많은 다른 응용 프로그램에 포함 된 라이브러리에서 사용되며 일부는 라이브러리에 사용됩니다. 아주 나쁜 모양. 라이브러리에는 이미 많은 종속성이 있습니다. 히스토리는 도입 된 각 의존성이 애플리케이션의 유지 보수를 더욱 어렵게한다는 것을 입증했습니다. 버전 충돌 등을 소개합니다. 이것은 내가 의존성을 없애고 새로운 것을 도입하지 말아야한다는 것을 의미합니다. – hyperman

답변

3

이 문제를 해결하는 주류 방법은 특정 타이머 스레드를 사용하여 지정된 간격으로 캐시를 새로 고치는 것입니다. 그러나 새 스레드를 만들 필요가 없기 때문에 생각할 수있는 구현은 의사 타이밍에 의한 캐시 새로 고침입니다. 기본적으로 캐시 접근자를 넣고 (put 및 get 메서드) 클라이언트가이 메서드를 사용할 때마다 캐시를 ​​넣거나 수행하기 전에 캐시를 새로 고쳐야하는지 확인합니다. 이 거친 생각입니다 :

class YourCache { 

    // holds the last time the cache has been refreshed in millis 
    private volatile long lastRefreshDate; 

    // indicates that cache is currently refreshing entries 
    private volatile boolean cacheCurrentlyRefreshing; 

    private Map cache = // Your concurrent map cache... 

    public void put(Object key, Object element) { 
    if (cacheNeedsRefresh()) { 
     refresh(); 
    } 
    map.put(key, element); 
    } 

    public Object get(Object key) { 
    if (cacheNeedsRefresh()) { 
     refresh(); 
    } 
    return map.get(key); 
    } 

    private boolean cacheNeedsRefresh() { 
    // make sure that cache is not currently being refreshed by some 
    // other thread. 
    if (cacheCurrentlyRefreshing) { 
     return false; 
    } 
    return (now - lastRefreshDate) >= REFRESH_INTERVAL; 
    } 

    private void refresh() { 
    // make sure the cache did not start refreshing between cacheNeedsRefresh() 
    // and refresh() by some other thread. 
    if (cacheCurrentlyRefreshing) { 
     return; 
    } 

    // signal to other threads that cache is currently being refreshed. 
    cacheCurrentlyRefreshing = true; 

    try { 
     // refresh your cache contents here 
    } finally { 
     // set the lastRefreshDate and signal that cache has finished 
     // refreshing to other threads. 
     lastRefreshDate = System.currentTimeMillis(); 
     cahceCurrentlyRefreshing = false; 
    } 
    } 
} 

는 개인적으로 난과 같이 그 일을 생각하지,하지만 당신이 원하는하지 않거나 타이머 스레드를 작성할 수없는 경우 다음이 당신을위한 옵션이 될 수 있습니다.

이 구현은 잠금을 피할 수 있지만 경쟁 이벤트로 인해 새로 고침을 반복하는 경향이 있습니다. 이것이 당신의 요구 사항에 대해 괜찮 으면 아무 문제가 없어야합니다. 그러나보다 엄격한 요구 사항이있는 경우 스레드를 올바르게 동기화하고 경쟁 이벤트를 피하기 위해 잠금을 설정해야합니다.

+0

get은 아무 것도 반환하지 않을 때만 put이 수행된다는 것을 제외하고는 어떤 일이 발생합니다. 따라서, 나는 다음과 같이 질문을 재 형성 할 것이다 : cacheNeedsRefresh의 실행 가능한 구현을 제공하라. 주요 문제는 lastRefreshDate가 휘발성이거나 메서드가 sycnhronized 또는 이와 비슷한 형식 일 필요가 있고 스레드가 많은 환경에 있다는 것입니다. – hyperman

+0

이것은 Guava가 일부 캐시 맵에서 수행하는 작업입니다. IIRC BTW –

+0

귀하의 의견에 따라 코드를 업데이트했습니다. 귀하의 요구 사항을 요구하는 것은 모르겠지만 일반적으로 휘발성 변수를 사용하는 것이 성능상의 불이익이 될 것이라고 생각하지 않습니다 (최소한 잠금과 비교할 때). 어쨌든, 나는 자물쇠 또는 휘발성 변수를 사용하지 않고 자바에서 이것을하는 또 다른 방법을 생각할 수 없다. 퍼포먼스가 휘발성 읽기 및 쓰기의 사용을 정말로 용납 할 수없는 문제인 경우 다른 동시 프로그래밍 모델 (Erlang 또는 Scala의 액터)을 사용하는 다른 기술을 살펴볼 것입니다. –