java LRUCache

java LRUCache

package org.rx.cache;

import org.rx.common.*;
import org.rx.beans.DateTime;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;

import static org.rx.common.Contract.as;
import static org.rx.common.Contract.require;
import static org.rx.util.AsyncTask.TaskFactory;

public final class LRUCache extends Disposable {
    private class CacheItem {
        public TV            value;
        private int          expireSeconds;
        private DateTime     createTime;
        private Consumer expireCallback;

        public CacheItem(TV value, int expireSeconds, Consumer expireCallback) {
            this.value = value;
            this.expireSeconds = expireSeconds;
            this.expireCallback = expireCallback;
            refresh();
        }

        public void refresh() {
            if (expireSeconds > -1) {
                createTime = DateTime.utcNow();
            }
        }

        public void callback() {
            if (expireCallback != null) {
                expireCallback.accept(value);
            }
        }
    }

    private static final Lazy> instance = new Lazy<>(() -> new LRUCache<>(1000, 120));

    public static LRUCache getInstance() {
        return instance.getValue();
    }

    public static Object getOrStore(String key, Function supplier) {
        require(key, supplier);

        return getInstance().getOrAdd(App.cacheKey(key), p -> supplier.apply(key));
    }

    private final Map cache;
    private int                      maxCapacity;
    private int                      expireSeconds;
    private Consumer             expireCallback;
    private Future                   future;

    public LRUCache(int maxSize, int expireSecondsAfterAccess) {
        this(maxSize, expireSecondsAfterAccess, 8 * 1000, null);
    }

    public LRUCache(int maxSize, int expireSecondsAfterAccess, long checkPeriod, Consumer removeCallback) {
        cache = Collections.synchronizedMap(new LinkedHashMap(maxSize + 1, .75F, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                boolean remove = size() > maxCapacity;
                if (remove) {
                    eldest.getValue().callback();
                }
                return remove;
            }
        });
        maxCapacity = maxSize;
        expireSeconds = expireSecondsAfterAccess;
        expireCallback = removeCallback;
        future = TaskFactory.schedule(() -> {
            for (Map.Entry entry : NQuery.of(cache.entrySet()).where(p -> p.getValue().expireSeconds > -1
                    && DateTime.utcNow().addSeconds(-p.getValue().expireSeconds).after(p.getValue().createTime))) {
                entry.getValue().callback();
                cache.remove(entry.getKey());
            }
        }, checkPeriod);
    }

    @Override
    protected void freeObjects() {
        if (future != null) {
            future.cancel(true);
        }
        cache.clear();
    }

    public void add(TK key, TV val) {
        add(key, val, expireSeconds, expireCallback);
    }

    public void add(TK key, TV val, int expireSecondsAfterAccess, Consumer removeCallback) {
        require(key);

        cache.put(key, new CacheItem(val, expireSecondsAfterAccess, removeCallback));
    }

    public void remove(TK key) {
        remove(key, true);
    }

    public void remove(TK key, boolean destroy) {
        require(key);
        CacheItem remove = cache.remove(key);
        if (remove == null) {
            return;
        }

        AutoCloseable ac;
        if (destroy && (ac = as(remove.value, AutoCloseable.class)) != null) {
            try {
                ac.close();
            } catch (Exception ex) {
                Logger.error(ex, "Auto close error");
            }
        }
    }

    public TV get(TK key) {
        require(key);

        CacheItem item = cache.get(key);
        if (item == null) {
            return null;
        }
        item.refresh();
        return item.value;
    }

    public TV getOrAdd(TK key, Function supplier) {
        require(key, supplier);

        CacheItem item = cache.get(key);
        if (item == null) {
            cache.put(key, item = new CacheItem(supplier.apply(key), expireSeconds, expireCallback));
        } else {
            item.refresh();
        }
        return item.value;
    }
}

 

posted on 2019-02-06 09:53 RockyLOMO 阅读(...) 评论(...) 编辑 收藏

你可能感兴趣的:(java LRUCache)