定时任务是恶魔(一)

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> 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;
    }

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

你可能感兴趣的:(定时任务是恶魔(一))