自动过期对象 - 是否有更好的替代方案?

5
请看下面我创建的类,我打算将其用作自动过期对象。
public class SelfExpiringObject {

    private boolean expired;
    // other properties

    public void setValidity(final int seconds) {
        new Timer().schedule(new TimerTask() {
            public void run() {
                expired = true;
            }
        }, TimeUnit.SECONDS.toMillis(seconds));
    }

    public boolean isExpired() {
        return expired;
    }
}

有没有更好的替代方案可以建议呢?

想在规则引擎中用它来处理事件。其中一个场景是当收到事件时,它们会被放入会话中(使用具有自动过期属性的对象)。我希望它们仅在规则设置的有效期内存在于会话中。一旦它们过期了,它们就会从会话中移除。


1
你可以尝试使用guava的CacheBuilder,它具有自动过期参数。 - assylias
是的,但在这种情况下,我将不得不将对象放入缓存中。我正在使用Drools,在其中需要将对象放入/加载到其会话中。 - Sudhanshu Umalkar
2
我不是JAVA开发人员,但是Shirley,这将创建大量的计时器对象。您是否考虑过一个简单的DateTime字段(例如在构造时设置为未来10秒),并在IsExpired()中与DateTime.Now进行比较? - Aron
5个回答

8

每个定时器都会创建一个线程,这是非常昂贵的对象。我建议您只在对象中设置到期时间,并有一个定期删除过期对象的线程。

public class ExpiringObject {

    private long expiresMS;
    // other properties

    public void setValidity(final int seconds) {
        expiresMS = System.currentTimeMillis() + seconds * 1000;
    }

    public boolean isExpired() {
        return System.currentTimeMillis() >= expireMS;
    }
}

监视这些项的线程可以在其过期时更新Drools。


这是我可能考虑的选项之一。 - Sudhanshu Umalkar
@Sudhanshu 注意:监控线程可以在到期时执行任何您需要的操作。 - Peter Lawrey
1
使用这段代码,但是不使用监视线程,直接在规则文件中使用isExpired()方法。谢谢。 - Sudhanshu Umalkar

7
懒惰地计算到期时间:
private long expiryDate; // set in constructor

public boolean isExpired() {
    return System.currentTimeMillis() >= expiryDate;
}

不需要创建一个新线程。

我想在规则执行期间检查对象是否已过期。请考虑以下示例规则- 规则“某些规则” 当 $event:事件(expired == false) 然后 修改($event){setRetainTime(60)}; 结束 - Sudhanshu Umalkar
不错 - 你应该使用 System.nanotime() 来计算延迟。 - assylias
打败我40秒;-) - Vincent van der Weele
1
@Sudhanshu 你可以按照正常方式访问DRL中的属性 - 如果你有一个名为isExpired()的方法,就不需要一个叫做expired的字段。 - artbristol

4

无法将对象放入地图中。它们在Drools会话中加载。 - Sudhanshu Umalkar

3

你可以使用时间戳代替计时器:

public class SelfExpiringObject {
    private long timestamp;

    private boolean expired;
    // other properties

    public void setValidity(final int seconds) {
        timestamp = System.currentTimeMillis() + 1000 * seconds;
    }

    public boolean isExpired() {
        if (!expired) {
            expired = System.currentTimeMillis() >= timestamp;
        } 
        return expired;
    }
}

但在这种情况下,我必须调用isExpired()来更新该标志。如下所示,我想在.drl(规则)文件中使用它。 - Sudhanshu Umalkar

2
  • 如果您有多个事件,创建一个Timer是不太高效的。
  • ScheduledExecutor 更加健壮可靠。
  • 您的代码不是线程安全的,调用Timer之后调用 isExpired 的代码可能仍然看到false - 最简单的方法是将变量标记为volatile

因此,您的类可能看起来像这样(尽管我更喜欢另一种提出的方法,完全删除线程并改用时间戳):

public class SelfExpiringObject {

    private final ScheduledExecutorService scheduler;
    private volatile boolean expired = false;
    // other properties

    public SelfExpiringObject(ScheduledExecutorService scheduler) {
        this.scheduler = scheduler;
    }

    public void setValidity(final int seconds) {
        scheduler.schedule(new Runnable() {
            public void run() {
                expired = true;
            }
        }, seconds, TimeUnit.SECONDS);
    }

    public boolean isExpired() {
        return expired;
    }
}

创建ScheduledExecutorService:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(poolSize);

这是我可能会尝试的东西,这也有助于控制可以生成的线程数量。该代码将由Drools引擎调用。 - Sudhanshu Umalkar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接