Java进程内缓存

Posted Just DO IT by luckygxf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java进程内缓存相关的知识,希望对你有一定的参考价值。

今天和同事聊到了缓存,在Java中实现进程缓存。这里主要思想是,用一个map做缓存。缓存有个生存时间,过期就删除缓存。这里可以考虑两种删除策略,一种是起一个线程,定期删除过期的key。第二个是,剔除模式,比较懒,访问到某个key的时候才,才去检查这个key是否过期,过期删除。

首先,对要缓存的value做了层封装,带了个时间戳

/**
 * Created by gxf on 2017/6/28.
 */
public class ValueWithTimeStamp<V>{
    private long expireTime;
    private V value;

    public ValueWithTimeStamp(long expireTime, V value) {
        this.expireTime = expireTime;
        this.value = value;
    }

    public long getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(long expireTime) {
        this.expireTime = expireTime;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }
}

ok,起线程定期删除策略的模式

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by gxf on 2017/6/28.
 * 使用线程,定期删除过期的key
 */
public class CacheMap<K, V>  {
    //这里使用ConcurrentHashMap避免,clean的时候,主线程修改chache,造成异常,
    //使用ConcurrentHashMap可以控制并发修改cache
    private Map<K, ValueWithTimeStamp<V>> cache = new ConcurrentHashMap<K, ValueWithTimeStamp<V>>();
    private static boolean cleanTaskIsRunning = false;

    //ttl 过期时间 单位:秒
    public void put(K key, V value, int ttl){
        long expireTime = System.currentTimeMillis() + ttl * 1000;
        ValueWithTimeStamp<V> valueWithTimeStamp = new ValueWithTimeStamp<>(expireTime, value);
        cache.put(key, valueWithTimeStamp);
        if(!cleanTaskIsRunning){
            startCleanTask();
            cleanTaskIsRunning = !cleanTaskIsRunning;
        }
    }

    public V get(K key){
        ValueWithTimeStamp<V> valueWithTimeStamp = cache.get(key);
        return null == valueWithTimeStamp ? null : valueWithTimeStamp.getValue();
    }

    /**
     * 启动清理线程
     * */
    private void startCleanTask(){
        Thread cleanThread = new Thread(new CleanTask());
        cleanThread.start();
    }

    /**
     * 清理过期的key
     * */
    class CleanTask implements Runnable{
        public void run(){
            while(true){
                long currentTime =  System.currentTimeMillis();
                //遍历map
                for(Map.Entry<K, ValueWithTimeStamp<V>> entry : cache.entrySet()){
                    ValueWithTimeStamp<V> valueWithTimeStamp = entry.getValue();
                    long expireTime = valueWithTimeStamp.getExpireTime();
                    //过期时间到了
                    if(currentTime > expireTime){
                        System.out.println("key : " + entry.getKey() + " expired ");
                        cache.remove(entry.getKey());
                    } //if
                } //for
                //每隔1s扫描map对象
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } //while
        }
    }
}

注意,这里需要使用ConcurrentHashMap做缓存。不然会出现多线线程操作map对象的异常

第二种策略,剔除模式

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by 58 on 2017/6/28.
 * 这里使用懒惰模式,获取key的时候,才剔除过期的key
 */
public class CacheMap1<K, V> {
    private Map<K, ValueWithTimeStamp<V>> cache = new ConcurrentHashMap<K, ValueWithTimeStamp<V>>();

    public void put(K key, V value, int ttl){
        long expireTime = System.currentTimeMillis() + ttl * 1000;
        ValueWithTimeStamp<V> valueWithTimeStamp = new ValueWithTimeStamp<V>(expireTime, value);
        cache.put(key, valueWithTimeStamp);
    }

    public V get(K key){
        ValueWithTimeStamp<V> valueWithTimeStamp = cache.get(key);
        long expireTime = valueWithTimeStamp.getExpireTime();
        long currentTime = System.currentTimeMillis();
        //key已经过期,删除,返回null
        if(currentTime > expireTime){
            System.out.println("key :" + key + " is expired.");
            cache.remove(key);
            return null;
        }

        return cache.get(key).getValue();
    }
}

项目中用到了redis,用这种方式应该会更快,这两天优化一下代码

以上是关于Java进程内缓存的主要内容,如果未能解决你的问题,请参考以下文章

自命为缓存之王的Caffeine

java 简单的代码片段,展示如何将javaagent附加到运行JVM进程

LINUX PID 1和SYSTEMD PID 0 是内核的一部分,主要用于内进换页,内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程,PID 为 1,又叫超级进程(代码片段

线程学习知识点总结

Redis缓存:java语言注释符号,附超全教程文档

如何在用java创建的布局内创建地图片段(GoogleMap)?