java本地定时缓存,单例可执行外部接口

环境:

有些对外提供的工具模块不是很好引用reids依赖,需要使用本地缓存,本来行想省事直接使用hutool的超时缓存-TimedCache,但是这个工具是真的坑逼,到时间调用get方法还是能获取到数据(吸取教训,一些工具类大厂有的尽量优先使用大厂的工具类,再考虑hutool,当然hutool其他有些工具确实很方便)。

实现的功能:

(1)普通使用:缓存数据并配置有效时间,可设置默认时间自动清除缓存,也可以自己设置。
(2)进阶使用:除了到时自动清除外,还需要他执行其他操作,比如 文件分片上传功能,到时间自动执行取消上传接口,删除已经上传的部分数据,避免一直占用存储空间,此外要求单例。(生产环境不建议用这种方式,可以考虑用定时消息或者延时消息去做)

pom依赖

    <!--工具类依赖-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.7.19</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.76</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>

普通使用:

工具类

直接上代码:

import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

public class TimeExpiredPoolCache {
    private static  long defaultCachedMillis = 10 * 60 * 1000L;//过期时间默认10分钟
    private static long timerMillis = 10 * 60 * 1000L;//定时清理默认10分钟
    /**
     * 对象池
     */
    private static ConcurrentHashMap<String, DataWrapper<?>> dataPool = null;
    /**
     * 对象单例
     */
    private static TimeExpiredPoolCache instance = null;
    private TimeExpiredPoolCache() {
        dataPool = new ConcurrentHashMap<String, DataWrapper<?>>();
    }
    private static synchronized void syncInit() {
        if (instance == null) {
            instance = new TimeExpiredPoolCache();
            initTimer();
        }
    }
    public static TimeExpiredPoolCache getInstance() {
        if (instance == null) {
            syncInit();
        }
        return instance;
    }
    /**
     * 定时器定时清理过期缓存
     */
    private static Timer timer = new Timer();
    private static void initTimer() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    clearExpiredCaches();
                } catch (Exception e) {
                    //logger.error("clearExpiredCaches error.", e);
                }
            }
        }, timerMillis, timerMillis);
    }

    /**
     * 缓存数据
     * @param key key值
     * @param data 缓存数据
     * @param cachedMillis 过期时间
     * @param dataRenewer 刷新数据
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T put(String key, T data, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
        DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
        if (data == null && dataRenewer != null) {
            data = dataRenewer.renewData();
        }
        //当重新获取数据为空,直接返回不做put
        if (data == null){
            return null;
        }
        if (dataWrapper != null) {
            //更新
            dataWrapper.update(data, cachedMillis);
        } else {
            dataWrapper = new DataWrapper<T>(data, cachedMillis);
            dataPool.put(key, dataWrapper);
        }
        return data;
    }
    /**
     * 直接设置缓存值和时间
     * @param key
     * @param data
     * @param cachedMillis
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T put(String key, T data, long cachedMillis) throws Exception{
        DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
        if (dataWrapper != null) {
            //更新
            dataWrapper.update(data, cachedMillis);
        } else {
            dataWrapper = new DataWrapper<T>(data, cachedMillis);
            dataPool.put(key, dataWrapper);
        }
        return data;
    }
    /**
     * 默认构造时间的缓存数据
     * @param key
     * @param data
     * @param dataRenewer
     * @return
     */
    @Deprecated
    public <T> T put(String key, T data, DataRenewer<T> dataRenewer) throws Exception {
        return put(key, data, defaultCachedMillis, dataRenewer);
    }
    /**
     * 获取缓存
     * @param key
     * @param cachedMillis
     * @param dataRenewer
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
        DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
        if (dataWrapper != null && !dataWrapper.isExpired()) {
            return dataWrapper.data;
        }
        return put(key, null, cachedMillis, dataRenewer);
    }
    @SuppressWarnings("unchecked")
    public <T> T get(String key) throws Exception {
        DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
        if (dataWrapper != null && !dataWrapper.isExpired()) {
            return dataWrapper.data;
        }
        return null;
    }
    /**
     * 清除缓存
     */
    public void clear() {
        dataPool.clear();
    }

    /**
     * 删除指定key的value
     * */
    public void remove(String key){
        dataPool.remove(key);
    }

    /**
     * 数据封装
     */
    private class DataWrapper<T> {
        /**
         * 数据
         */
        private T data;
        /**
         * 到期时间
         */
        private long expiredTime;
        /**
         * 缓存时间
         */
        private long cachedMillis;
        private DataWrapper(T data, long cachedMillis) {
            this.update(data, cachedMillis);
        }
        public void update(T data, long cachedMillis) {
            this.data = data;
            this.cachedMillis = cachedMillis;
            this.updateExpiredTime();
        }
        public void updateExpiredTime() {
            this.expiredTime = System.currentTimeMillis() + cachedMillis;
        }
        /**
         * 数据是否过期
         * @return
         */
        public boolean isExpired() {
            if (this.expiredTime > 0) {
                return System.currentTimeMillis() > this.expiredTime;
            }
            return true;
        }
    }
    /**
     * 数据构造
     */
    public interface DataRenewer<T> {
        public T renewData();
    }

    /**
     * 清除过期的缓存
     */
    private static void clearExpiredCaches() {
        List<String> expiredKeyList = new LinkedList<String>();

        for(Entry<String, DataWrapper<?>> entry : dataPool.entrySet()){
            if (entry.getValue().isExpired()) {
                expiredKeyList.add(entry.getKey());
            }
        }
        for (String key : expiredKeyList) {
            dataPool.remove(key);
        }
    }
	}
测试:
import com.example.demo2.utils.TimeExpiredPoolCache;
import org.springframework.web.bind.annotation.*;

@RestController
public class CacheDemo {

    @RequestMapping(value = "/saveToken", method = RequestMethod.GET)
    public String  saveToken(){
        try {
            TimeExpiredPoolCache.getInstance().put("token","1008611",1000L*30);
            new TimeThread().run();
            return "保存token进入缓冲成功";
        } catch (Exception e) {
            return "保存token进入缓存失败";
        }
    }

    @RequestMapping(value = "/getToken", method = RequestMethod.GET)
    public String  getToken(){
        try {
           // token = String.valueOf(TimeExpiredPoolCache.getInstance().get("token"));
            String   token = TimeExpiredPoolCache.getInstance().get("token");
            if (null!=token){
                return token;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "数据为空................";
    }

    public static class TimeThread implements  Runnable{

        @Override
        public void run() {
            for(int i=30;i>0;i--){
                System.out.println("缓冲倒计时:"+i+" 秒");
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

浏览直接访问 (我这里设置的端口号是8888)
在这里插入图片描述
localhost:8888/saveToken 调用存储token值为1008611,时间为30秒有效
在这里插入图片描述
localhost:8888/getToken 可以获取token值,可以在缓存有效期内和有效期外分别调用,看下缓存值是否存在
在这里插入图片描述
在这里插入图片描述

进阶使用:

思路是传一个接口对象参数进去,比较简单,直接上代码

工具类:
import com.nw.test.service.CacheCleanService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 超时池缓存
 *
 * @author ppp
 * @date 2022/10/9
 */
public class TimeCacheUtil {
    /**
     * 定时清理默认1天
     */
    private static final long DEFAULT_TIMER_MILLIS = TimeUnit.DAYS.toMillis(1);
    /**
     * 对象池
     */
    private static ConcurrentHashMap<String, DataWrapper<?>> dataPool = null;
    /**
     * 对象单例
     */
    private static TimeCacheUtil instance = null;
    /**
     * 定时清理过期缓存定时器
     */
    private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(3,
            new BasicThreadFactory.Builder().namingPattern("cache-schedule-pool-%d").daemon(true).build());

    private TimeCacheUtil() {
        dataPool = new ConcurrentHashMap<String, DataWrapper<?>>();
    }

    private static synchronized void syncInit() {
        if (instance == null) {
            instance = new TimeCacheUtil();
        }
    }

    public static TimeCacheUtil getInstance() {
        if (instance == null) {
            syncInit();
        }
        return instance;
    }


    /**
     * 设置缓存值和时间
     *
     * @param key          键
     * @param data         数据
     * @param cachedMillis 缓存时间,单位:毫秒
     * @return 数据
     */
    @SuppressWarnings("unchecked")
    public <T> T put(String key, T data, long cachedMillis) {
        DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
        if (dataWrapper != null) {
            // 更新
            dataWrapper.update(data, cachedMillis);
        } else {
            dataWrapper = new DataWrapper<T>(data, cachedMillis);
            dataPool.put(key, dataWrapper);
            initTimer(key, cachedMillis, null);
        }
        return data;
    }

    /**
     * 设置缓存值并执行外部其他操作
     *
     * @param key               键
     * @param data              数据
     * @param cachedMillis      缓存时间,单位:毫秒
     * @param cacheCleanService 超时清除服务,允许为空,这个类自己定义,传个实现类的对象进来即可
     * @return 数据
     */
    @SuppressWarnings("unchecked")
    public <T> T put(String key, T data, long cachedMillis, CacheCleanService cacheCleanService) {
        DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
        if (dataWrapper != null) {
            // 更新
            dataWrapper.update(data, cachedMillis);
        } else {
            dataWrapper = new DataWrapper<T>(data, cachedMillis);
            dataPool.put(key, dataWrapper);
            initTimer(key, cachedMillis, cacheCleanService);
        }
        return data;
    }

    /**
     * 定时器定时清理过期缓存
     *
     * @param key               键
     * @param timeOut           超时时间,默认一天,单位:毫秒
     * @param cacheCleanService 缓存清理服务
     */
    private static void initTimer(String key, long timeOut, CacheCleanService cacheCleanService) {
        long timerOutMillis = ObjectUtils.isEmpty(timeOut) ? DEFAULT_TIMER_MILLIS : timeOut;
        executorService.schedule(new Runnable() {
            @Override
            public void run() {
                if (dataPool.containsKey(key)) {
                    clearExpiredCaches(key);
                    if (null != cacheCleanService && StringUtils.isNotBlank(key)) {
                         // 自定义清除服务的方法
                        cacheCleanService.clean(key);
                    }
                }
            }
        }, timerOutMillis, TimeUnit.MILLISECONDS);
    }


    @SuppressWarnings("unchecked")
    public <T> T get(String key) {
        DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
        if (dataWrapper != null && !dataWrapper.isExpired()) {
            return dataWrapper.data;
        }
        return null;
    }

    /**
     * 清除缓存
     */
    public void clear() {
        dataPool.clear();
    }

    /**
     * 删除指定key的value
     */
    public void remove(String key) {
        dataPool.remove(key);
    }

    /**
     * 是否包含key
     *
     * @param key 键
     * @return boolean
     */
    public boolean containsKey(String key) {
        return dataPool.containsKey(key);
    }

    /**
     * 数据封装
     */
    private class DataWrapper<T> {
        /**
         * 数据
         */
        private T data;
        /**
         * 到期时间
         */
        private long expiredTime;
        /**
         * 缓存时间
         */
        private long cachedMillis;

        private DataWrapper(T data, long cachedMillis) {
            this.update(data, cachedMillis);
        }

        public void update(T data, long cachedMillis) {
            this.data = data;
            this.cachedMillis = cachedMillis;
            this.updateExpiredTime();
        }

        public void updateExpiredTime() {
            this.expiredTime = System.currentTimeMillis() + cachedMillis;
        }

        /**
         * 数据是否过期
         *
         * @return
         */
        public boolean isExpired() {
            if (this.expiredTime > 0) {
                return System.currentTimeMillis() > this.expiredTime;
            }
            return true;
        }
    }

    /**
     * 清除过期的缓存
     */
    private static void clearExpiredCaches() {
        List<String> expiredKeyList = new LinkedList<>();
        for (Map.Entry<String, DataWrapper<?>> entry : dataPool.entrySet()) {
            if (entry.getValue().isExpired()) {
                expiredKeyList.add(entry.getKey());
            }
        }
        for (String key : expiredKeyList) {
            dataPool.remove(key);
        }
    }

    /**
     * 清除过期的缓存
     */
    private static void clearExpiredCaches(String key) {
        dataPool.remove(key);
    }
}

自定义的接口CacheCleanService,按需定义即可

public interface CacheCleanService {
   public void  clean(String key);
}

实现类

@Service
public class CacheCleanServiceImpl implements CacheCleanService {

@Override
public void clean(String key) {
    System.out.println("外部清理key: " + key + "   线程名称:" + Thread.currentThread().getName());
}

}

测试:

在这里插入图片描述

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值