All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cedarsoftware.util.LRUCache Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
package com.cedarsoftware.util;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;

import com.cedarsoftware.util.cache.LockingLRUCacheStrategy;
import com.cedarsoftware.util.cache.ThreadedLRUCacheStrategy;

/**
 * This class provides a thread-safe Least Recently Used (LRU) cache API that evicts the least recently used items once
 * a threshold is met. It implements the Map interface for convenience.
 * 

* This class offers two implementation strategies: a locking approach and a threaded approach. *

    *
  • The Locking strategy can be selected by using the constructor that takes only an int for capacity, or by using * the constructor that takes an int and a StrategyType enum (StrategyType.LOCKING).
  • *
  • The Threaded strategy can be selected by using the constructor that takes an int and a StrategyType enum * (StrategyType.THREADED). Additionally, there is a constructor that takes a capacity, a cleanup delay time, * and a ScheduledExecutorService.
  • *
*

* The Locking strategy allows for O(1) access for get(), put(), and remove(). For put(), remove(), and many other * methods, a write-lock is obtained. For get(), it attempts to lock but does not lock unless it can obtain it right away. * This 'try-lock' approach ensures that the get() API is never blocking, but it also means that the LRU order is not * perfectly maintained under heavy load. *

* The Threaded strategy allows for O(1) access for get(), put(), and remove() without blocking. It uses a ConcurrentHashMap * internally. To ensure that the capacity is honored, whenever put() is called, a thread (from a thread pool) is tasked * with cleaning up items above the capacity threshold. This means that the cache may temporarily exceed its capacity, but * it will soon be trimmed back to the capacity limit by the scheduled thread. *

* LRUCache supports null for both key and value. *

* Special Thanks: This implementation was inspired by insights and suggestions from Ben Manes. * @see LockingLRUCacheStrategy * @see ThreadedLRUCacheStrategy * @see LRUCache.StrategyType * @author John DeRegnaucourt ([email protected]) *
* Copyright (c) Cedar Software LLC *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* License *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class LRUCache implements Map { private final Map strategy; public enum StrategyType { THREADED, LOCKING } /** * Create a "locking-based" LRUCache with the passed in capacity. * @param capacity int maximum number of entries in the cache. * @see com.cedarsoftware.util.cache.LockingLRUCacheStrategy */ public LRUCache(int capacity) { strategy = new LockingLRUCacheStrategy<>(capacity); } /** * Create a "locking-based" OR a "thread-based" LRUCache with the passed in capacity. *

* Note: There is a "shutdown" method on LRUCache to ensure that the default scheduler that was created for you * is cleaned up, which is useful in a container environment. * @param capacity int maximum number of entries in the cache. * @param strategyType StrategyType.LOCKING or Strategy.THREADED indicating the underlying LRUCache implementation. * @see com.cedarsoftware.util.cache.LockingLRUCacheStrategy * @see com.cedarsoftware.util.cache.ThreadedLRUCacheStrategy */ public LRUCache(int capacity, StrategyType strategyType) { if (strategyType == StrategyType.THREADED) { strategy = new ThreadedLRUCacheStrategy<>(capacity, 10, null); } else if (strategyType == StrategyType.LOCKING) { strategy = new LockingLRUCacheStrategy<>(capacity); } else { throw new IllegalArgumentException("Unsupported strategy type: " + strategyType); } } /** * Create a "thread-based" LRUCache with the passed in capacity. *

* Note: There is a "shutdown" method on LRUCache to ensure that the default scheduler that was created for you * is cleaned up, which is useful in a container environment. If you supplied your own scheduler and cleanupPool, * then it is up to you to manage their termination. The shutdown() method will not manipulate them in any way. * @param capacity int maximum number of entries in the cache. * @param cleanupDelayMillis int number of milliseconds after a put() call when a scheduled task should run to * trim the cache to no more than capacity. The default is 10ms. * @param scheduler ScheduledExecutorService which can be null, in which case one will be created for you, or you * can supply your own. If one is created for you, when shutdown() is called, it will be shutdown * for you. * @see com.cedarsoftware.util.cache.ThreadedLRUCacheStrategy */ public LRUCache(int capacity, int cleanupDelayMillis, ScheduledExecutorService scheduler) { strategy = new ThreadedLRUCacheStrategy<>(capacity, cleanupDelayMillis, scheduler); } @Override public V get(Object key) { return strategy.get(key); } @Override public V put(K key, V value) { return strategy.put(key, value); } @Override public void putAll(Map m) { strategy.putAll(m); } @Override public V remove(Object key) { return strategy.remove(key); } @Override public void clear() { strategy.clear(); } @Override public int size() { return strategy.size(); } @Override public boolean isEmpty() { return strategy.isEmpty(); } @Override public boolean containsKey(Object key) { return strategy.containsKey(key); } @Override public boolean containsValue(Object value) { return strategy.containsValue(value); } @Override public Set> entrySet() { return strategy.entrySet(); } @Override public Set keySet() { return strategy.keySet(); } @Override public Collection values() { return strategy.values(); } @Override public String toString() { return strategy.toString(); } @Override public int hashCode() { return strategy.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } LRUCache other = (LRUCache) obj; return strategy.equals(other.strategy); } public void shutdown() { if (strategy instanceof ThreadedLRUCacheStrategy) { ((ThreadedLRUCacheStrategy) strategy).shutdown(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy