Java 시간 기반 맵/캐시(만료된 키 포함)
Java Map이나 이와 유사한 표준 데이터 스토어에 대해 알고 계신 분이 있습니까?이러한 데이터 스토어는 타임아웃이 되면 자동으로 엔트리를 삭제합니다.즉, 에이징을 의미합니다.에이징은 오래된 기한이 지난 엔트리가 자동으로 "에이징 아웃"됩니다.
Maven을 통해 접근할 수 있는 오픈 소스 라이브러리로 하는 것이 좋습니다.
저는 그 기능을 실장하는 방법을 알고 있고, 과거에 몇 번인가 실장했기 때문에, 그 점에 대해 조언을 구하는 것이 아니라, 좋은 레퍼런스 실장을 위한 포인트에 대해 묻고 있습니다.
WeakHashMap과 같은 WeakReference 기반 솔루션은 옵션이 아닙니다.이는 키가 인터네셔널되지 않은 문자열일 가능성이 높고 가비지 컬렉터에 의존하지 않는 구성 가능한 타임아웃을 원하기 때문입니다.
또한 Ehcache는 외부 구성 파일이 필요하기 때문에 믿고 싶지 않은 옵션입니다.코드 전용 솔루션을 찾고 있습니다.
네, 구글 컬렉션, 혹은 구아바에는 MapMaker라고 불리는 것이 있습니다.이것이 바로 그것을 가능하게 합니다.
ConcurrentMap<Key, Graph> graphs = new MapMaker()
.concurrencyLevel(4)
.softKeys()
.weakValues()
.maximumSize(10000)
.expiration(10, TimeUnit.MINUTES)
.makeComputingMap(
new Function<Key, Graph>() {
public Graph apply(Key key) {
return createExpensiveGraph(key);
}
});
업데이트:
guava 10.0(2011년 9월 28일 출시) 현재, 이러한 MapMaker 메서드의 대부분은 새로운 CacheBuilder를 위해 폐지되었습니다.
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
이것은 같은 요건에 대해서 실시한 샘플의 실장입니다.동시성이 양호하게 동작합니다.누군가에게 유용할 수도 있어요.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author Vivekananthan M
*
* @param <K>
* @param <V>
*/
public class WeakConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 1L;
private Map<K, Long> timeMap = new ConcurrentHashMap<K, Long>();
private long expiryInMillis = 1000;
private static final SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss:SSS");
public WeakConcurrentHashMap() {
initialize();
}
public WeakConcurrentHashMap(long expiryInMillis) {
this.expiryInMillis = expiryInMillis;
initialize();
}
void initialize() {
new CleanerThread().start();
}
@Override
public V put(K key, V value) {
Date date = new Date();
timeMap.put(key, date.getTime());
System.out.println("Inserting : " + sdf.format(date) + " : " + key + " : " + value);
V returnVal = super.put(key, value);
return returnVal;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
for (K key : m.keySet()) {
put(key, m.get(key));
}
}
@Override
public V putIfAbsent(K key, V value) {
if (!containsKey(key))
return put(key, value);
else
return get(key);
}
class CleanerThread extends Thread {
@Override
public void run() {
System.out.println("Initiating Cleaner Thread..");
while (true) {
cleanMap();
try {
Thread.sleep(expiryInMillis / 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void cleanMap() {
long currentTime = new Date().getTime();
for (K key : timeMap.keySet()) {
if (currentTime > (timeMap.get(key) + expiryInMillis)) {
V value = remove(key);
timeMap.remove(key);
System.out.println("Removing : " + sdf.format(new Date()) + " : " + key + " : " + value);
}
}
}
}
}
Git Repo Link (청취자 구현 포함)
https://github.com/vivekjustthink/WeakConcurrentHashMap
건배!!
Apache Commons에는 Map의 엔트리를 만료시키는 데코레이터가 있습니다.PassiveExpiringMap은 Guava의 캐시보다 간단합니다.
추신, 조심해요. 동기화되지 않았어요.
자기 기한이 만료되는 해시 맵의 실장을 시험해 볼 수 있습니다.이 구현에서는 만료된 엔트리를 삭제하기 위해 스레드를 사용하지 않고 모든 작업에서 자동으로 정리되는 DelayQueue를 사용합니다.
ehcache는 원하는 것에 대해 과잉인 것처럼 들리지만 외부 구성 파일은 필요하지 않습니다.
일반적으로 컨피규레이션을 선언형 컨피규레이션파일로 이동하는 것이 좋습니다(따라서 새로운 설치로 다른 유효기간이 필요할 경우 재컴파일할 필요가 없습니다).단, 이는 필수가 아닙니다.프로그래밍으로 설정할 수도 있습니다.http://www.ehcache.org/documentation/user-guide/configuration
Google 컬렉션(guava)에는 MapMaker가 있습니다. MapMaker에는 기한(만료)을 설정할 수 있으며 공장 출하 방법을 사용하여 선택한 인스턴스를 만들 때 소프트 또는 약한 참조를 사용할 수 있습니다.
Apache MINA Project의 수업에서 Expiring Map http://www.java2s.com/Code/Java/Collections-Data-Structure/ExpiringMap.htm을 사용해 볼 수 있습니다.
간단한 것을 필요로 하는 유저가 있는 경우는, 다음의 간단한 키 익스피리언스 세트를 참조해 주세요.그것은 쉽게 지도로 변환될지도 모른다.
public class CacheSet<K> {
public static final int TIME_OUT = 86400 * 1000;
LinkedHashMap<K, Hit> linkedHashMap = new LinkedHashMap<K, Hit>() {
@Override
protected boolean removeEldestEntry(Map.Entry<K, Hit> eldest) {
final long time = System.currentTimeMillis();
if( time - eldest.getValue().time > TIME_OUT) {
Iterator<Hit> i = values().iterator();
i.next();
do {
i.remove();
} while( i.hasNext() && time - i.next().time > TIME_OUT );
}
return false;
}
};
public boolean putIfNotExists(K key) {
Hit value = linkedHashMap.get(key);
if( value != null ) {
return false;
}
linkedHashMap.put(key, new Hit());
return true;
}
private static class Hit {
final long time;
Hit() {
this.time = System.currentTimeMillis();
}
}
}
일반적으로 캐시는 오브젝트를 일정 시간 동안 보관해야 하며 나중에 오브젝트를 노출해야 합니다.오브젝트를 보관하는 타이밍은 사용 사례에 따라 달라집니다.난 이 일을 단순하게 하고 싶었고, 스레드나 스케줄러가 없었으면 했다.이 방법은 나에게 효과가 있다.와는 달리SoftReference
오브젝트는 최소한의 시간 동안 사용할 수 있습니다.그러나 태양이 적색 거성으로 변할 때까지 기억에 머무르지 마십시오.
사용 예로서 요구가 최근에 이루어졌는지 확인할 수 있어야 하며, 그 경우 바쁜 사용자가 버튼을 여러 번 눌러도 요청된 액션을 두 번 수행하지 않아야 하는 느린 응답 시스템을 생각해 보십시오.다만, 그 후에 같은 조치가 요구되었을 경우에는 다시 실시한다.
class Cache<T> {
long avg, count, created, max, min;
Map<T, Long> map = new HashMap<T, Long>();
/**
* @param min minimal time [ns] to hold an object
* @param max maximal time [ns] to hold an object
*/
Cache(long min, long max) {
created = System.nanoTime();
this.min = min;
this.max = max;
avg = (min + max) / 2;
}
boolean add(T e) {
boolean result = map.put(e, Long.valueOf(System.nanoTime())) != null;
onAccess();
return result;
}
boolean contains(Object o) {
boolean result = map.containsKey(o);
onAccess();
return result;
}
private void onAccess() {
count++;
long now = System.nanoTime();
for (Iterator<Entry<T, Long>> it = map.entrySet().iterator(); it.hasNext();) {
long t = it.next().getValue();
if (now > t + min && (now > t + max || now + (now - created) / count > t + avg)) {
it.remove();
}
}
}
}
Guava 캐시는 구현이 용이합니다.guava 캐시를 사용하여 타임베이스로 키를 만료할 수 있습니다.나는 투고를 완전히 읽었고 아래는 내 연구의 열쇠를 준다.
cache = CacheBuilder.newBuilder().refreshAfterWrite(2,TimeUnit.SECONDS).
build(new CacheLoader<String, String>(){
@Override
public String load(String arg0) throws Exception {
// TODO Auto-generated method stub
return addcache(arg0);
}
}
참조: guava 캐시의 예시
언급URL : https://stackoverflow.com/questions/3802370/java-time-based-map-cache-with-expiring-keys
'source' 카테고리의 다른 글
Vuejs - v-for를 사용하여 어레이의 모든 고유 값을 가져오는 방법(중복 제거) (0) | 2022.08.08 |
---|---|
Nuxt Auth Module - ID/사용자명으로 사용자를 취득하는 방법 (0) | 2022.08.08 |
Java 8 스트림을 중단하거나 각 스트림에서 반환하시겠습니까? (0) | 2022.08.08 |
Laravel Passport 및 클라이언트 자격 증명으로 자체 API를 사용하는 방법 (0) | 2022.08.08 |
Vuejs. 하위 구성 요소의 프로펠을 변환해도 경고가 트리거되지 않습니다.왜 그럴까 (0) | 2022.08.08 |