All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mxixm.fastboot.weixin.util.CacheMap Maven / Gradle / Ivy

The newest version!
package com.mxixm.fastboot.weixin.util;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用来存储短暂对象的缓存类,实现Map接口,内部有一个定时器用来清除过期(30秒)的对象。
 * 扩展了两个功能
 */
public class CacheMap extends AbstractMap {

    // 12个小时
    private static final long DEFAULT_TIMEOUT = 24 * 60 * 60 * 1000;

    private static final Map cacheNameMap = new ConcurrentHashMap<>();

    // 十分钟扫一次缓存
    private static final long DEFAULT_CLEAR_PERIOD = 10 * 60 * 1000;

    private static TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            cacheNameMap.values().forEach(v -> clearTimeoutCache(v));
        }
    };

    // 守护线程timer
    private static Timer timer = new Timer(true);

    static {
        // 清理线程可优化为多个
        timer.schedule(timerTask, DEFAULT_CLEAR_PERIOD, DEFAULT_CLEAR_PERIOD);
    }

    public static  Builder builder() {
        return new Builder<>();
    }

    static class CacheEntry implements Entry {
        long time;
        K key;
        V value;

        CacheEntry(K key, V value) {
            super();
            this.key = key;
            this.value = value;
            this.time = System.currentTimeMillis();
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            return this.value = value;
        }
    }


    public static void clearTimeoutCache(CacheMap map) {
        long now = System.currentTimeMillis();
        Object[] keys = map.keySet().toArray();
        for (Object key : keys) {
            CacheEntry entry = (CacheEntry) map.getEntryMap().get(key);
            if (now - entry.time >= map.getCacheTimeout()) {
                map.remove(key);
            }
        }
    }

    /**
     * 清理超过限制的缓存
     * @param map
     */
    public static void clearOverflowCache(CacheMap map) {
        int maxSize = map.maxSize;
        int realMaxSize = maxSize + maxSize / 4;
        if (map.size() > realMaxSize) {
            int needRemove = map.size() - maxSize;
            TreeSet sortedSet = map.entrySet();
            Entry e;
            while ((e = sortedSet.pollFirst()) != null && needRemove-- > 0) {
                map.remove(e.getKey());
            }
        }
    }

    private long cacheTimeout;

    private Map entryMap = new ConcurrentHashMap<>();

    private String cacheName;

    /**
     * 在读取的时候是否刷新时间
     */
    private boolean refreshOnRead;

    /**
     * 0的时候是无限,最大空间,当然允许超过最大空间,但是只能超过四分之一的量,这样做是为了保证性能
     */
    private int maxSize;

    public CacheMap(String cacheName, long timeout, boolean refreshOnRead, int maxSize) {
        this.cacheName = cacheName;
        this.cacheTimeout = timeout;
        this.refreshOnRead = refreshOnRead;
        this.maxSize = maxSize;
        cacheNameMap.put(cacheName, this);
    }

    public CacheMap(String cacheName, long timeout, boolean refreshOnRead) {
        this(cacheName, timeout, refreshOnRead, 0);
    }

    public CacheMap(String cacheName, long timeout) {
        this(cacheName, timeout, false);
    }

    public CacheMap(String cacheName) {
        this(cacheName, DEFAULT_TIMEOUT);
    }

    public Map getEntryMap() {
        return entryMap;
    }

    public long getCacheTimeout() {
        return cacheTimeout;
    }

    /**
     * remove其实是通过这个来实现的,但是这里功能明显导致删除会失败,结果会内存溢出,所以我重写了remove方法
     * 这里的entrySet实现可能是有点问题的,因为返回的是一个新的实例。
     * @return dummy
     */
    @Override
    public TreeSet> entrySet() {
        TreeSet> entrySet = new TreeSet<>(Comparator.comparing(kvEntry -> ((CacheEntry)kvEntry).time));
        Set> wrapEntrySet = entryMap.entrySet();
        for (Entry entry : wrapEntrySet) {
            entrySet.add(entry.getValue());
        }
        return entrySet;
    }

    @Override
    public int size() {
        return entryMap.size();
    }

    @Override
    public V remove(Object key) {
        Entry entry = this.entryMap.remove(key);
        if (entry == null) {
            return null;
        }
        return entry.getValue();
    }

    @Override
    public V get(Object key) {
        CacheEntry entry = entryMap.get(key);
        if (entry == null) {
            return null;
        }
        if (refreshOnRead) {
            entry.time = System.currentTimeMillis();
        }
        return entry == null ? null : entry.value;
    }

    @Override
    public V put(K key, V value) {
        // 如果超量,需要清理
        if (maxSize > 0 && this.size() > maxSize + maxSize / 4) {
            clearOverflowCache(this);
        }
        CacheEntry entry = new CacheEntry(key, value);
        entryMap.put(key, entry);
        return value;
    }

    public static class Builder {
        private long cacheTimeout;
        private Map entryMap;
        private String cacheName;
        private boolean refreshOnRead;
        private int maxSize;

        Builder() {
        }

        public CacheMap.Builder cacheTimeout(long cacheTimeout) {
            this.cacheTimeout = cacheTimeout;
            return this;
        }

        public CacheMap.Builder cacheName(String cacheName) {
            this.cacheName = cacheName;
            return this;
        }

        public CacheMap.Builder refreshOnRead() {
            this.refreshOnRead = true;
            return this;
        }

        public CacheMap.Builder maxSize(int maxSize) {
            this.maxSize = maxSize;
            return this;
        }

        public CacheMap build() {
            return new CacheMap<>(cacheName, cacheTimeout, refreshOnRead, maxSize);
        }

        public String toString() {
            return "com.mxixm.fastboot.weixin.util.CacheMap.CacheMapBuilder(cacheTimeout=" + this.cacheTimeout + ", entryMap=" + this.entryMap + ", cacheName=" + this.cacheName + ", refreshOnRead=" + this.refreshOnRead + ", maxSize=" + this.maxSize + ")";
        }
    }


    public static void main(String[] args) {
        CacheMap map = CacheMap.builder().cacheName("test").cacheTimeout(1000).maxSize(5).refreshOnRead().build();
        map.put("1", "2");
        map.put("2", "3");
        map.put("3", "4");
        map.put("4", "5");
        map.put("5", "6");
        map.get("1");
        map.put("6", "7");
        map.put("7", "8");
        map.put("8", "9");
        map.put("9", "10");
    }

}






© 2015 - 2025 Weber Informatics LLC | Privacy Policy