如何在Java中实现有效的超时处理

3
有`n`个对象执行某些操作。在执行操作后,会更新时间戳。现在我想要实现一个超时线程,验证时间戳是否比例如60秒更旧。
我的第一个解决方案是使用一个线程(while循环+休眠),它持有包括最后时间戳的所有对象列表。现在我遇到了一个问题,即线程需要花费59秒加上休眠时间才能决定超时的最坏情况。
我正在寻找像计时器这样的解决方案,其中可以更新延迟时间。
有什么想法吗?

2
我很难理解你的逻辑。你能否展示一段样例代码来说明你想要做什么? - Muhammad Gelbana
有些东西让我觉得你想要监视一些线程的“工作完成”事件。你可以使用 CountDownLatch 来实现这个目的。 - gkuzmin
当一个对象在时间内完成时会发生什么?而当它们没有完成时会发生什么?您让它们继续吗?还是它们需要停止?有几个选项。您可以在问题中添加您的目标,以获得最佳解决方案。 - maasg
另外澄清一下:当你说“有n个对象执行某些操作…”时,我猜你的意思是“n个线程…”吧? - maasg
3个回答

2
我认为使用带有wait/notify的监视器对象是合理的(如果您使用JDK >= 5,则可以使用await/signal与Condition)。
思路很简单:
工作线程:
doYourActualWork();
synchronized(jobFinishedMonitor) {
    updateTimestamp();

    jobFinishedMonitor.notify();
}

超时线程:

synchronized(jobFinishedMonitor) {
    while(within60Second(timestamp)) {
        jobFinishedMonitor.wait(60);
    }
    if (within60Second(timestamp)) {
        timeoutHappened=true;
    }
 }
 if (timeoutHappened) {
     // do timeout handling
 }

应该使用 wait(60_000) 来等待 60 秒。 - Sina Madani

1
关于这个问题,不清楚您想要如何使用超时。这里给您提供两种实现轻量级超时的选项:监控和控制。 监控超时 对于全局计时器,您可以使用JDK中的Timer工具:
public TimeoutTask implements TimerTask {
    List<MonitorableObject>  objects;
    public TimeoutTask(List<MonitorableObject> objects) {
        // make sure you can share this collection concurrently, 
        // e.g. copyonwritearraylist
        this.objects = objects;
    }
    public void run() {
       // objects -> filter(isTimeOut(currentTime)) -> do something
    }
}

Timer timer = new Timer();
timer.schedule(new TimeoutTask(myObjectList), 0,60*1000); // repeat each 60secs

可以使用 ScheduledExecutorService 实现类似的构造:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// Note that I can use here TimeoutTask b/c TimerTask is a Runnable - 
// this is just for the example. You'd better implement a plain runnable.
scheduler.schedule(new TimeoutTask(myObjectList), 60, TimeUnit.SECONDS); 

我更喜欢使用ScheduledExecutorService而不是Timer,因为SchedulerExecutor可以持有线程池。此外,底层线程池可以用于调用scheduledExecutorService.execute(...)进行立即并发执行(非计划),使其成为通用执行器设施,而不是专用定时器功能。

在两种情况下,您都需要特别注意安全地从您正在监视的对象中获取超时值。通常,您将使用对象中的同步方法来询问其超时状态。

强制超时

ExecutorService为您提供了一个API,以在给定的超时时间内执行一组任务。例如:

List<Callable<?>> myTasks = ...;
// populate myTasks with Callable`s that wrap your intended execution

ExecutorService executorService = ... ;

List<Future<?>> results = executorService.invokeAll(myTasks, 60, TimeUnit.SECONDS);

在此方法返回后,您可以在给定的时间内询问每个Future是否成功。


0
每次更新时间戳时中断线程。然后它将循环,找不到要做的事情,睡眠,并且如果时间戳没有发生其他变化,则过期。如果第二次被中断,那就更好了。并且在任何时候,它都不应该睡眠超过60减去(当前时间减去最旧的时间戳)的时间。

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