美文网首页
定时任务是恶魔(一)

定时任务是恶魔(一)

作者: 黄云斌huangyunbin | 来源:发表于2018-10-29 10:25 被阅读0次

    cache是个好东西,guava的lodingcache是好东西。

    问题1:如何自定义过期逻辑

    但是lodingcache只能基于访问时间的过期,假如有其他过期逻辑的时候,还是要自己写。

    这就开始了我的坑爹之旅。

    我有个LocalLock类,是对java的ReentrantLock的封装,ReentrantLock在使用的时候自然是不能过期回收的。

    有对应的api:reentrantLock.isLocked()

        public boolean canUse() {
            long noUseTime = System.currentTimeMillis() - lastUseTime;
            if (noUseTime > canUseTime && !reentrantLock.isLocked()) {
                return false;
            }
            return true;
        }
    

    然后我就很高兴的写个定时任务来清除过期的了。

            this.schedule.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    checkNoUse();
                }
            }, 10, 10, TimeUnit.SECONDS);
    
        private void checkNoUse() {
            Iterator<Map.Entry<String, LocalLock>> iterator = cache.entrySet().iterator();
            while (iterator.hasNext()) {
                LocalLock lock = iterator.next().getValue();
                if (!lock.canUse()) {
                    iterator.remove();
                }
            }
        }
    

    问题2:这个是有并发问题的
    • 时序1 定时任务线程可以回收的
    • 时序2 业务线程加锁变成不可回收了
    • 时序3 定时任务线程remove删除

      这样就把不该回收的对象删除了。
    解决办法

    synchronized

        public synchronized boolean canUse() {
    

    这个synchronized对应两个场景,一个是定时任务,一个就是从缓存中拿对象

    private LocalLock getLock(String key) {
            LocalLock lock = cache.get(key);
            if (lock != null && lock.canUse()) {
                return lock;
            }
    

    问题3:发现还是有并发问题

    从缓存中拿到对象还是可用的,正要开始使用,定时任务判断过期了,回收掉。。

    解决办法:

    缓存中拿对象的同时,修改最后使用时间,保证一段时间内不会被定时任务回收。

    愉快的修改代码为:

    public synchronized boolean canUseAndRefresh() {
            long noUseTime = System.currentTimeMillis() - lastUseTime;
            if (noUseTime > canUseTime && !reentrantLock.isLocked()) {
                return false;
            }
            lastUseTime = System.currentTimeMillis();       
            return true;
        }
    

    问题4:发现定时任务永远回收不了

    因为定时任务每次都修改了最后访问时间,相当于定时任务在续命了,永远不会过期。

    解决办法:

    canUseAndRefresh加个参数区分两个逻辑,定时任务就不修改最后访问时间了。

    public synchronized boolean canUseAndRefresh(boolean refresh) {
            long noUseTime = System.currentTimeMillis() - lastUseTime;
            if (noUseTime > canUseTime && !reentrantLock.isLocked()) {
                return false;
            }
            if (refresh) {
                lastUseTime = System.currentTimeMillis();
            }
            return true;
        }
    

    整个方案还是比较复杂的,而且可以看到有很多坑,那还有其他的解决办法吗?

    相关文章

      网友评论

          本文标题:定时任务是恶魔(一)

          本文链接:https://www.haomeiwen.com/subject/qrivtqtx.html