게시판 즐겨찾기
편집
드래그 앤 드롭으로
즐겨찾기 아이콘 위치 수정이 가능합니다.
[Java] 캐시 클래스.
게시물ID : programmer_23282짧은주소 복사하기
작성자 : 봄아
추천 : 4
조회수 : 1339회
댓글수 : 3개
등록시간 : 2021/09/03 17:14:25

백엔드 작업 하면서 내부 변동성 없는 데이터를 DB를 통해 아주 자주~ 자주 호출 하는 구분이 생겼다.

캐싱 어노테이션을 이용해 보려 했지만 그낙 내 맘에 들지는 않았다.

일정 시간이 지나면 원하는 것만 캐시 내용이 제거 됬으면 하는 내 소망이였다. 그래서 만들었다. 



import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 일정 시간 지나면 항목 제거 Map<P>
 * 항목을 추가하면 내부에 runnable이 생성되고 interval 시간에 삭제 처리가 이뤄 진다.
 * @param <K>
 * @param <V>
 * @see {@link RemoveEventListener}
 */
public class CacheIntervalMap<K, V> extends ConcurrentHashMap<K, V>{

    private static final long serialVersionUID = -3116825266165827645L;
    
    /** 스케줄러 서비스 */
    private final ScheduledExecutorService scheduleExe;
    
    /** 지속 시간 */
    private final long interval;
    
    /** 지속 시간 단위 */
    private final TimeUnit unit;
    
    /** 자동 삭제하기 전 문의하기 이벤트 리스너 */
    private final RemoveEventListener<K, V> listener;

    /** 중간 취소 처리 위한 관리 객체 */
    private final Map<K, Future<K>> callManage;
    
    /**
     * 일정 시간이 지나면 제거
     * @param <K>
     * @param <V>
     */
    private static class RemoverCashIntervalMap<K, V> implements Callable<K>{
        
        private final CacheIntervalMap<K, V> owner;
        
        private final K key;
        
        public RemoverCashIntervalMap(CacheIntervalMap<K, V> owner, K key) {
            this.owner = owner;
            this.key = key;
        }
        
        @Override
        public K call() throws Exception {

           System.out.println("자동 삭제 " + this.key );

            if( ! this.owner.containsKey(this.key) )
                return this.key;
            
            // 일단 지우고 나서 리스너로 문의해 본다.
            final V value = this.owner.remove(this.key);
            
            if( this.owner.listener == null )
                return this.key;
            
            if( ! this.owner.listener.isRemove(this.key, value) ) {
                // 이미 삭제된거 어쩔 수 없다. 다시 추가~!
                this.owner.put(this.key, value);
            }
            
            return this.key;
        }
    }
    
    /**
     * 삭제하기
     * @param interval 일정시간
     * @param unit 시간 단위
     */
    public CacheIntervalMap(long interval, TimeUnit unit) {
        this(interval, unit, null);
    }
    
    /**
     * 삭제하기
     * @param interval 일정시간
     * @param unit 시간 단위
     * @param listener 삭제할지 말지 결정.
     */
    public CacheIntervalMap(long interval, TimeUnit unit, RemoveEventListener<K, V> listener) {
        super();
        this.interval = interval;
        this.unit = unit;
        this.listener = listener;
        
        this.scheduleExe = Executors.newSingleThreadScheduledExecutor();
        this.callManage = new ConcurrentHashMap<K, Future<K>>();
    }
    
    @Override
    public V remove(Object key) {
        this.callManage.remove( key ).cancel(false);
        return super.remove(key);
    }
    
    @Override
    public V put(K key, V value) {
        
        this.callManage.put(key, this.scheduleExe.schedule( new RemoverCashIntervalMap<K,V>(this, key), interval, unit) );
        
        return super.put(key, value);
    }
    
    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        
        for( K key : m.keySet() ) {
            this.callManage.put(key, this.scheduleExe.schedule( new RemoverCashIntervalMap<K,V>(this, key), interval, unit) );
        }
        
        super.putAll(m);
    }
    
    @Override
    public void clear() {
        
        for( Future<K> k : this.callManage.values() ) {
            k.cancel(true);
        }
        
        this.callManage.clear();
        
        super.clear();
    }
    
}

이벤트 클래스

 /**
 * 삭제 되기 전 발생하는 이벤트
 * @param <K>
 * @param <V>
 * @see {@link CacheIntervalMap}
 */
public interface RemoveEventListener<K, V> {

    /**
     * 삭제 되기전 문의 하기~
     * @param key
     * @param value
     * @return true: 삭제해라. false:다음에 삭제할까 생각해 볼께.
     */
    public boolean isRemove(K key, V value);
}


사용 예제...
 
final Map<String, Integer> testExe = new CacheIntervalMap<String, Integer>(5, TimeUnit.SECONDS, new RemoveEventListener<String, Integer>() {
            
            @Override
            public boolean isRemove(String key, Integer value) {
                // BB는 삭제하지 않는다.
                return !"BB".equals(key);
            }
            
        });
        
        testExe.put("AA", 100);
        testExe.put("BB", 200);
        testExe.put("CC", 300);


결과

자동 삭제 AA
자동 삭제 BB
자동 삭제 CC
자동 삭제 BB
자동 삭제 BB
자동 삭제 BB

5초마다 계속 삭제 시도는 하지만 BB는 지워지지 않는다.

뭐 ... 아주 많은 데이터를 구겨 넣으면 문제가 되겠지만 몇 백개의 작은 객체는 충분히 버틸만 하다.
 
출처 내 머리.
전체 추천리스트 보기
새로운 댓글이 없습니다.
새로운 댓글 확인하기
글쓰기
◀뒤로가기
PC버전
맨위로▲
공지 운영 자료창고 청소년보호