BACKEND.*/JAVA
LinkedHashMap에서 accessOrder true일때 주의할 점
초코푸딩
2025. 1. 31. 21:45
Get 할 때도 lock 또는 syncronized 사용해야 한다! 이유는 아래에..
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
private final ReentrantLock lock = new ReentrantLock();
public LRUCache(int maxSize) {
super(maxSize, 0.75f, true); // accessOrder = true (최근 사용된 순서 유지)
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize; // 캐시 크기를 초과하면 가장 오래된 항목 제거
}
// Lock을 사용하여 동기화된 put()
public void putSafe(K key, V value) {
lock.lock();
try {
super.put(key, value);
} finally {
lock.unlock();
}
}
// Lock을 사용하여 동기화된 get()
public V getSafe(K key) {
lock.lock();
try {
return super.get(key);
} finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>(2);
Runnable task1 = () -> {
for (int i = 0; i < 5; i++) {
cache.putSafe(i, "Value" + i);
System.out.println(Thread.currentThread().getName() + " PUT " + i);
}
};
Runnable task2 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " GET " + cache.getSafe(i));
}
};
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
}
}
메서드 | 동기화 필요 여부 | 이유 |
put() | ✅ 필요 | 내부적으로 연결 리스트를 변경하기 때문 |
get() (accessOrder = false) | ❌ 필요 없음 | 단순 읽기 연산이므로 안전함 |
get() (accessOrder = true) | ✅ 필요 | recordAccess()를 호출하여 연결 리스트를 수정하기 때문 |
즉, 기본적으로 put()은 동기화해야 하지만, accessOrder = true인 경우에는 get()도 동기화하는 것이 안전함.
recordAccess() 메서드는 요소가 접근될 때마다(= get() 또는 put()이 호출될 때) 내부적인 연결 리스트를 조정하기 위해 사용되기 때문이다.
void recordAccess(Map<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>) m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.head);
}
}
- accessOrder가 true인 경우(즉, LRU 캐시 모드인 경우), 최근 접근된 엔트리를 리스트의 마지막으로 이동합니다.
- remove()를 통해 기존 위치에서 엔트리를 제거한 뒤,
- addBefore()를 호출하여 새로운 위치에 삽입합니다.