Java 单机缓存事件驱动实现

关键词:Java单机缓存事件驱动

大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)



有时候我们自己写一个小项目陶冶一下情操,在需要用到缓存的时候,就有些纠结了,基本缓存都是使用第三服务,或者一个很大的Jar包。也就是一个小单机项目,我们也得部署一套缓存应用,引用一些Jar包…



既然没有,那么我们自己动手撸一个,娱乐一下~


说到缓存,一般采用ConcurrentHashMapput get remove操作,对应缓存的存入、获取、移除。
然后Map的值,使用一个类,将数据包裹起来,包裹类里面添加存活时间属性。
在执行get操作时,判断存活时间是否超时:超时移除,返回空;否则返回数据;
最后用另外一个线程,定时遍历Map检查失效数据、并移除。


但是!


在多线程的情况下,非常容易出现问题!!!

ConcurrentHashMap虽然是操作线程安全,但是在组合的情况下,不是线程安全的!!

比喻:检查缓存中是否存在计数器A,如果存在,就加1,再存入缓存;否则创建一个存入缓存;

在多线程情况下,判断存在和存入缓存,是分为2个操作;

在初始情况下,甲、乙、丙同时检查缓存,都没有计数器A;然后甲、乙、丙分别创建了计数器,最终计数器结果为1,而不是理论值3!


加锁可以避免这种情况,但是变成了串行执行,效率会降低很多。

这个时候就要祭出事件驱动模型了~



Redis、NodeJs、Javascript都是单线程,但是它们执行效率非常高,原因就在于事件驱动

通俗来讲:有事@我,没事我就闲着;多个事一起来了,排好队,一个一个来,one by one;

这样始终只有一个人(线程)在工作,不存在多个人(线程)协同工作互相扯皮。



把对缓存的所有操作,抽象为一个个事件
线程需要执行缓存操作,就往队列放入一个事件,然后线程在事件结果返回之前,一直阻塞等待;
多线程情况下,仍然是往队列放事件。保证队列是线程安全就行;

再看缓存工作者(缓存线程):
当队列里面有东西(事件)的时候,缓存线程从队列取出事件,然后根据事件类型,执行对应操作。
当结果操作完缓存得到结果后,再唤醒对应的线程,把结果传给线程,线程获取结果放行。缓存线程再重复从队列里取事件

队列事件取完之后,再检查一下距离上次定时清理失效数据的事件是否超过一个阀值:如果是,就执行遍历ConcurrentHashMap清理失效数据。或者是继续等待队列出东西。


如果有组合操作,保证组合操作的事件,存入队列时是连续的就行。(或者使用责任链模式:当前事件中,包含下一个事件,缓存线程在执行完该事件后,再检查一下是否包含下一个事件,形成一个事件链



import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 单机缓存管理
 * @author bugcat
 * */
public class InnerCache {

    /**
     * 1 秒
     */
    public final static int sec = 1000;
    /**
     * 1 分钟
     */
    public final static int min = 60000;
    /**
     * 1 小时
     */
    public final static int hour = 3600000;
    /**
     * 1 天
     */
    public final static int day = 86400000;

    
    
    //所有操作组成的阻塞队列
    private BlockingQueue<ExecuteEntry> queue = new LinkedBlockingQueue();
    
    //缓存
    private ConcurrentHashMap<String, Wrapper> cacheMap = new ConcurrentHashMap<>(16000);
    
    private boolean run = true;

    
    //单例
    private static InnerCache cache = new InnerCache();
    public static InnerCache getManager() {
        return cache;
    }
    
    private InnerCache() {
        //缓存线程
        Thread worker = new Worker(this);
        worker.start();
    }


    /**
     * 包裹器,把数据对象抽象成统一的缓存节点。从缓存中存入、获取的原始数据
     * */
    private static class Wrapper {

        private Object data;        //缓存数据
        private long keepAlive;     //保留截止时间

        public Wrapper() {}
        
        public Wrapper(long activeTime) {
            this.setActiveTime(activeTime);
        }
        public Wrapper(long activeTime, Object data) {
            this.data = data;
            this.setActiveTime(activeTime);
        }

        //设置保留时限 毫秒
        private void setActiveTime(long activeTime) {
            this.keepAlive = System.currentTimeMillis() + activeTime;
        }
        
    }
    
    
    
    
    
    
    /**
     * 从缓存中获取
     * */
    public <T> T get(String key, Class<T> clazz) {
        ExecuteEntry entry = ExecuteEntry.query(key);       //查询事件
        queue.offer(entry);                                 //存入队列
        Wrapper wrapper = entry.getResult();                //阻塞等待结果
        return (T) wrapper.data;
    }

    /**
     * 从缓存中获取,并且马上刷新缓存时间
     * */
    public <T> T getAndRefresh(String key, long keepAlive, Class<T> clazz) {
        ExecuteEntry entry = ExecuteEntry.refresh(key, keepAlive);      //刷新缓存事件
        queue.offer(entry);
        Wrapper wrapper = entry.getResult();
        return (T) wrapper.data;
    }

    /**
     * 设置缓存
     * */
    public void set(String key, long keepAlive, Object object) {
        ExecuteEntry entry = ExecuteEntry.insert(key, keepAlive, object);   //设置缓存事件
        queue.offer(entry);
        entry.getResult();
    }

    /**
     * 如果不存在就设置缓存,否则忽略
     * */
    public void setIfAbsent(String key, long keepAlive, Object object){
        ExecuteEntry query = ExecuteEntry.query(key);       //这个是组合操作:查询 + 存入,如果有其他的组合操作,可以按照这个模式写
        query.next = queryResult -> {                       //queryResult 查询事件返回的结果 = Wrapper
            query.nextEntry = null;                         //下一个事件对象
            if( queryResult.data == null ){                 //如果缓存中数据为null,表示需要把object存入缓存
                query.nextEntry = ExecuteEntry.insert(key, keepAlive, object);
                // query.nextEntry.next = xxx -> {}	事件链
            }
            return query.nextEntry;                         //返回下一个事件对象
        };
        queue.offer(query);
        query.getResult();
    }
    
    /**
     * 删除缓存
     * */
    public void del(String key) {
        ExecuteEntry entry = ExecuteEntry.remove(key);      //移除缓存事件
        queue.offer(entry);
        entry.getResult();
    }



    
    
    /**
     * 事件
     * */
    private static abstract class ExecuteEntry {
        
        //接收缓存线程处理的结果
        private BlockingQueue<Wrapper> callback = new ArrayBlockingQueue<>(1);
        
        //缓存key
        protected String key;
        
        protected Wrapper wrapper;
        
        //创建下一个事件对象
        protected Function<Wrapper, ExecuteEntry> next;
        
        //下一个事件对象,由next创建赋值
        protected ExecuteEntry nextEntry;
        

        public static ExecuteEntry query(String key){
            ExecuteEntry entry = new QueryExecuteEntry();
            entry.key = key;
            return entry;
        }
        
        public static ExecuteEntry refresh(String key, long keepAlive){
            ExecuteEntry entry = new RefreshExecuteEntry();
            entry.key = key;
            entry.wrapper = new Wrapper(keepAlive);
            return entry;
        }
        
        
        public static ExecuteEntry insert(String key, long keepAlive, Object object){
            ExecuteEntry entry = new InsertKeepExecuteEntry();
            entry.key = key;
            entry.wrapper = new Wrapper(keepAlive, object);
            return entry;
        }
        
        public static ExecuteEntry remove(String key){
            ExecuteEntry entry = new RemoveExecuteEntry();
            entry.key = key;
            return entry;
        }


        /**
         * 阻塞等待结果,结果一定不为null
         * */
        public Wrapper getResult(){
            
            Wrapper wrapper = null;

            //如果有下一个事件对象,先取下一个事件对象
            if( nextEntry != null ){    
                nextEntry.getResult();
            }
            
            //阻塞等待结果
            try { wrapper = callback.take(); } catch ( InterruptedException e ) {  }
            
            if( wrapper == null ) {
                wrapper = new Wrapper();
            }
            
            return wrapper;
        }

        /**
         * 阻塞等待结果,结果可能为null
         * */
        public <T> T getData(Class clazz){
            Wrapper wrapper = getResult();
            return (T) wrapper.data;
        }

        /**
         * 缓存线程回调方法
         * */
        public Wrapper callback(Map<String, Wrapper> cacheMap){
            Wrapper result = execute(cacheMap);     //执行指定事件的具体方法,此时仍然属于缓存线程作用范围,单线程
            callback.offer(result);                 //将结果通知给线程,结束线程阻塞
            return result;
        }

        /**
         * 事件具体操作
         * */
        protected abstract Wrapper execute(Map<String, Wrapper> cacheMap);
    }
    

    
    
    private static class QueryExecuteEntry extends ExecuteEntry {
        @Override
        public Wrapper execute(Map<String, Wrapper> cacheMap) {
            Wrapper wrapper = cacheMap.get(key);
            if( wrapper == null ){
                wrapper = new Wrapper();
            }
            if( wrapper.keepAlive < System.currentTimeMillis() ){   //存活时间小于当前时间
                cacheMap.remove(key);
            }
            return wrapper;
        }
    }


    private static class RefreshExecuteEntry extends ExecuteEntry {
        @Override
        public Wrapper execute(Map<String, Wrapper> cacheMap) {
            Wrapper wrapper = cacheMap.get(key);
            if( wrapper == null ){
                wrapper = new Wrapper();
            }
            if( wrapper.keepAlive < System.currentTimeMillis() ){   //存活时间小于当前时间
                cacheMap.remove(key);
            }
            wrapper.keepAlive = super.wrapper.keepAlive;
            return wrapper;
        }
    }


    private static class InsertKeepExecuteEntry extends ExecuteEntry {
        @Override
        public Wrapper execute(Map<String, Wrapper> cacheMap) {
            cacheMap.put(key, wrapper);
            return wrapper;
        }
    }


    private static class RemoveExecuteEntry extends ExecuteEntry {
        @Override
        public Wrapper execute(Map<String, Wrapper> cacheMap) {
            cacheMap.remove(key);
            return new Wrapper();
        }
    }
    
    
    /**
     * 缓存线程
     * */
    private static class Worker extends Thread {
        
        private InnerCache cache;

        public Worker(InnerCache cache) {
            this.cache = cache;
        }

        @Override
        public void run() {
            
            long now = 0L;
            long rolling = 3 * min;     //每隔rolling时间后,检查移除失效数据

            long last = System.currentTimeMillis();

            while ( cache.run ) {

                try {

                    //从队列中取事件
                    ExecuteEntry entry = cache.queue.poll(5000L, TimeUnit.MILLISECONDS);
                    
                    //如果取到事件,或者超过等待时间
                    while ( entry != null ){
                        
                        //执行事件回调
                        Wrapper result = entry.callback(cache.cacheMap);
                        
                        //检查是否包含下一个事件(事件链)
                        if( entry.next != null ){
                            
                            //执行next,获取下一个事件对象,循环执行
                            entry = entry.next.apply(result);
                            
                        } else {
                            
                            //如果再没有事件,结束循环
                            entry = null;
                        }
                    }
                    
                    //当前时间
                    now = System.currentTimeMillis();
                    
                    //当前时间 - 上次检查时间,超过了rolling,执行检查移除失效数据
                    if(  now - last > rolling){
                        
                        final long curr = now;
                        Stream<Map.Entry<String, Wrapper>> stream = null;
                        
                        //如果缓存中超过x个对象,使用并行Stream处理,否则使用串行Stream todo x值根据实际情况设置
                        if( cache.cacheMap.size() > 4000 ){
                            stream = cache.cacheMap.entrySet().parallelStream();
                        } else {
                            stream = cache.cacheMap.entrySet().stream();
                        }
                        
                        cache.cacheMap = stream
                                .filter( mapEntry -> mapEntry.getValue().keepAlive > curr)
                                .collect(Collectors.toConcurrentMap(mapEntry -> mapEntry.getKey(), 
                                        mapEntry -> mapEntry.getValue(),
                                        (mapEntry1, mapEntry2) -> mapEntry2,
                                        () -> new ConcurrentHashMap<>(6400)
                                ));
                        
                        last = now;     //更新检查时间
                    }

                } catch ( Exception e ) {
                    e.printStackTrace();
                }
            }
        }
        
    }

    
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        run = false;
    }

    
}






~   the end   ~





  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值