Спасибо всем вам, ребята , особенно пользователю " gid ", который дал идею.
Моя цель состояла в том, чтобы оптимизировать производительность для операции get ()Учитывая, что операция invalidate () будет вызываться очень редко.
Я написал тестовый класс, который запускает 16 потоков, каждый из которых вызывает get () - Операция миллион раз.С помощью этого класса я реализовал некоторую реализацию на своем 2-ядерном компьютере.
Результаты тестирования
Implementation Time
no synchronisation 0,6 sec
normal synchronisation 7,5 sec
with MapMaker 26,3 sec
with Suppliers.memoize 8,2 sec
with optimized memoize 1,5 sec
1) "Без синхронизации" не является поточно-ориентированным, но дает нам лучшую производительность, котораямы можем сравнить с.
@Override
public List<String> list() {
if (cache == null) {
cache = loadCountryList();
}
return cache;
}
@Override
public void invalidateCache() {
cache = null;
}
2) «Нормальная синхронизация» - довольно хорошая производительность, стандартная реализация без затруднений
@Override
public synchronized List<String> list() {
if (cache == null) {
cache = loadCountryList();
}
return cache;
}
@Override
public synchronized void invalidateCache() {
cache = null;
}
3) «с MapMaker» - очень низкая производительность.
См. Мой вопрос вверху для кода.
4) "with Suppliers.memoize" - хорошая производительность.Но в качестве производительности той же «Нормальной синхронизации» нам нужно ее оптимизировать или просто использовать «Нормальную синхронизацию».
См. Ответ пользователя «gid» для кода.
5) "с оптимизированной памятью" - производительность, сравнимая с реализацией "без синхронизации", но поточно-ориентированная.Это то, что нам нужно.
Сам класс кеша: (Используемые здесь интерфейсы поставщика взяты из библиотеки коллекций Google и имеют только один метод get (). См. http://google -коллекции.googlecode.com/svn/trunk/javadoc/com/google/common/base/Supplier.html)
public class LazyCache<T> implements Supplier<T> {
private final Supplier<T> supplier;
private volatile Supplier<T> cache;
public LazyCache(Supplier<T> supplier) {
this.supplier = supplier;
reset();
}
private void reset() {
cache = new MemoizingSupplier<T>(supplier);
}
@Override
public T get() {
return cache.get();
}
public void invalidate() {
reset();
}
private static class MemoizingSupplier<T> implements Supplier<T> {
final Supplier<T> delegate;
volatile T value;
MemoizingSupplier(Supplier<T> delegate) {
this.delegate = delegate;
}
@Override
public T get() {
if (value == null) {
synchronized (this) {
if (value == null) {
value = delegate.get();
}
}
}
return value;
}
}
}
Пример использования:
public class BetterMemoizeCountryList implements ICountryList {
LazyCache<List<String>> cache = new LazyCache<List<String>>(new Supplier<List<String>>(){
@Override
public List<String> get() {
return loadCountryList();
}
});
@Override
public List<String> list(){
return cache.get();
}
@Override
public void invalidateCache(){
cache.invalidate();
}
private List<String> loadCountryList() {
// this should normally load a full list from the database,
// but just for this instance we mock it with:
return Arrays.asList("Germany", "Russia", "China");
}
}