ConcurrentHashMap

ConcurrentHashMap은 Thread-Safe 합니다. 동시성 작업에 최적화된 Map 클래스이고, 이 자료구조를 이용해서 캐시를 구현할 수 있습니다. 

캐시에서 값을 가져오는 경우 ConcurrentHashMap 에서 해당 키를 사용하여 값을 가져올 수 있습니다. 또, 각 항목에 만료 시간 필드를 추가하여 만료 시간을 추적할 수 있습니다. 만료시간이 5분이라면 아래와 같이 코드를 작성할 수 있습니다.

ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
long expiryTime = System.currentTimeMillis() + (5 * 60 * 1000); // 5분 후
map.put("key1", new MyObject(expiryTime, ...)); // MyObject는 만료 시간을 포함하는 필드를 가지고 있음

그리고 맵에서 값을 가져올 때 해당 항목의 만료 시간을 확인하고 만료되었는지 여부도 확인해볼 수 있습니다.

Object value = map.get("key1");
if (value != null && ((MyObject) value).isExpired()) {
    map.remove("key1"); // 만료된 값 제거
}

다른 방법으로 스케줄러를 사용하여 만료된 항목을 제거할 수 있는데 매 주기마다 만료된 항목을 제거하는 스케줄러를 사용하는 방법이 있습니다. 5분마다 만료된 항목을 검사하고 제거하는 스케줄러를 만들 수 있습니다. 

ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        if (value != null && ((MyObject) value).isExpired()) {
            map.remove(key); // 만료된 값 제거
        }
    }
}, 5, 5, TimeUnit.MINUTES); // 매 5분마다 실행

Caffein Cache

Caffeine Cache는 Java 어플리케이션에서 메모리 기반 캐시를 구현하는 데 사용하는 라이브러리입니다. 

사용하는 방법은 가장 먼저 라이브러리를 추가해야 합니다.

dependencies {
    implementation 'com.github.ben-manes.caffeine:caffeine:<version>'
}

캐시 인스턴스를 생성해서 Caffeine 클래스를 제공할 수 있습니다. build() 메서드를 호출하게 되면 캐시 인스턴스를 생성하게 됩니다.

Cache<String, MyObject> cache = Caffeine.newBuilder()
        .maximumSize(100) // 최대 크기 설정
        .expireAfterAccess(5, TimeUnit.MINUTES) // 5분 동안 접근이 없는 데이터는 자동으로 제거됨
        .build(); // 캐시 인스턴스 생성

캐시의 값 및 추가, 검색할 수 있는 방법으로는 아래 처럼 조회해볼 수 있습니다.

cache.put("key1", new MyObject(...)); // key1에 MyObject 추가

캐시에서 값을 검색할 때는 get()메서드를 호출하게 됩니다. 

MyObject myObject = cache.get("key1");
if (myObject == null) {
    // 캐시에 값이 없는 경우
} else {
    // 캐시에서 값을 가져온 경우
}

캐시에 값을 제거하려면 invalidate() 혹은 invalidateAll 메서드를 호출하게 됩니다. 전자는 특정 키에 해당하는 값을 제거하고 후자는 모든 값을 제거하게 됩니다. 

cache.invalidate("key1"); // key1에 해당하는 값 제거
cache.invalidateAll(); // 모든 값 제거

 

인스턴스 캐싱

  • 싱글톤 패턴
    • 해당 클래스 인스턴스를 한번만 생성하여 사용할 수 있다. 자주 사용되는 객체를 캐싱하는 방법으로 메모리 사용량을 줄이고 성능을 향상시킬 수 있다. 멀티스레드에서 사용할 떄는 살짝 주의해야 한다. 
public class MySingleton {

    private static MySingleton instance = null;

    // private 생성자
    private MySingleton() {
    }

    // 인스턴스 반환 메서드
    public static synchronized MySingleton getInstance() {
        if (instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}
  • 팩토리 메서드 사용
    • 객체 생성을 하위 클래스에 위임하는 팩토리 메서드 패턴을 사용하여 구현해볼 수 있습니다. 
public abstract class MyFactory {
    private Map<String, MyObject> objectCache = new HashMap<>();

    public MyObject getObject(String key) {
        if (!objectCache.containsKey(key)) {
            objectCache.put(key, createObject(key));
        }
        return objectCache.get(key);
    }

    protected abstract MyObject createObject(String key);
}

public class MyObjectFactory extends MyFactory {
    @Override
    protected MyObject createObject(String key) {
        // key에 따라 MyObject 생성
    }
}
  • 캐시 라이브러리 이용
    • Guava Cache, Ehcache 등을 이용해서 캐시 라이브러리를 이용해볼 수 있습니다. 

 

 

'IT' 카테고리의 다른 글

Java 버전업 (11->17) 후기 켁;  (1) 2024.01.07
테스트 방법  (0) 2023.03.18
Spring Bean  (0) 2023.02.04
카프카 스터디를 시작하며  (0) 2022.12.03
넥스트스텝 - 레이싱 카 2단계  (0) 2022.01.30