2013-11-09 4 views
0

Я не совсем точно знаю, как это сделать ... так что может потребоваться несколько попыток, чтобы этот вопрос был правильным. У меня есть аннотация для кэширования результатов метода. Мой кодом является частным вилки на данный момент, но часть я работаю начинается здесь: https://code.google.com/p/cache4guice/source/browse/trunk/src/org/cache4guice/aop/CacheInterceptor.java#46AOP + синхронизированный

Я аннотированный метод, который я хочу кэшированный, который работает очень медленный запрос, иногда занимает несколько минут запустить. Проблема в том, что мое асинхронное веб-приложение продолжает привлекать новых пользователей и запрашивать одни и те же данные. Однако метод getSlowData() еще не завершен.

Так что-то вроде этого:

@Cached 
public getSlowData() { 
... 
} 

Внутри перехватчик, мы проверяем кэш и обнаружить, что это не кэшируются, который проходит мимо нас вниз:

return getResultAndCache(methodInvocation, cacheKey); 

Я никогда не получал комфортно со всей концепцией параллелизма. Я думаю, что мне нужно отметить, что метод getResultAndCache() для данного getSlowData() уже был запущен, а последующие запросы должны ждать результата.

Спасибо за любые мысли или советы!

+0

Это интересная проблема, так что блокирование в процессе работы для того же кеш-ключа кажется мне логичным. Возможно, вы могли бы проверить подход, применяемый к аналогичным проектным проектам Spring: абстракция Spring Cache, ehcache-spring-annotations и spring-modules-cache. – samlewis

+0

Внесите свой перехватчик с помощью одноточечного 'ConcurrentHashMap' методов, которые уже начали обработку? – condit

+0

Спасибо за предложение, я просмотрел аннотации кэш Spring в пятницу. Я еще раз посмотрю другие проекты, чтобы увидеть, пропустил ли я что-то. Я думаю, что Весна использует волшебство, специфичное для Весны, для решения этой проблемы, это не было очевидно, глядя на их перехватчик, и тот, который он расширяет. – shawnjohnson

ответ

2

Большинство реализаций кэш-памяти синхронизируют вызовы с 'get' и 'set', но это только половина уравнения. То, что вам действительно нужно сделать, это убедиться, что только один поток входит в «проверить, загружена ли и загрузится, если нет». В большинстве случаев, стоимость сериализации доступа потоков может быть не стоит, если есть

1) no risk 
2) little cost 

загрузки данных несколько раз через параллельные потоки (комментарий здесь, если вам нужно больше разъяснений по этому вопросу). Так как эта аннотаций используется повсеместно, я бы предложил создать вторую аннотацию, что-то вроде «@ThreadSafeCached» и метод Invoke будет выглядеть следующим образом

Object cacheElement = cache.get(cacheKey); 
    if (cacheElement != null) {    
     LOG.debug("Returning element in cache: {}", cacheElement);    
    } else { 
     synchronized(<something>) { 
       // double-check locking, works in Java SE 5 and newer 
      if ((cacheElement = cache.get(cacheKey)) == null) { 
       // a second check to make sure a previous thread didn't load it 
       cacheElement = getResultAndCache(methodInvocation, cacheKey); 
      } else { 
       LOG.debug("Returning element in cache: {}", cacheElement);    
      } 
     } 
    } 
    return cacheElement; 

Теперь я оставил часть о том, что вы синхронизировать. Было бы наиболее оптимально блокировать кэшируемый элемент, так как вы не будете создавать какие-либо потоки, которые не будут специально загружать этот элемент кэша. Если это невозможно, другой грубый подход может заключаться в том, чтобы заблокировать класс аннотации. Это, очевидно, менее эффективно, но если у вас нет контроля над логикой загрузки кеша (похоже, вы это делаете), это простой выход!

+0

Я забыл сказать, что в контексте webapps у вас также есть возможность предварительно загружать информацию, то есть загружать ее при запуске, чтобы вы вообще не попадали в условия гонки. Гораздо проще, чем пытаться сразиться с нитями IMO :) – HakunaM

+0

Спасибо @HakunaM, что сработало! Они были ключевыми для синхронизации, и в этом случае я использовал класс метода, например: 'synchronized (methodInvocation.getClass())' – shawnjohnson

Смежные вопросы