gpt4 book ai didi

java - 异步任务执行与DelayQueue : postponing an already submitted task

转载 作者:行者123 更新时间:2023-11-30 09:27:51 28 4
gpt4 key购买 nike

我有一个专用于执行异步任务的线程池,使用共享的 DelayQueue。

基本上,一切正常,除了一件事:我希望能够推迟执行一些已经安排好的任务。例如,假设我现在在时间 t=0 提交一个要在 30 秒内执行的任务。 10 秒后 (t=10),我决定,哦不,该任务不会在 t=30 执行,而是在 t=50;因此,我将其推迟到 20 秒后。

为此,我有 postpone 方法,它修改为任务设置的时间,从而更改 getDelay 的返回值。代码在本文末尾。

不幸的是,它不起作用。实际上很容易破坏系统并使过期元素保留在队列中,比它们通常应该保留的时间长得多。更具体地说,我观察到以下不良行为:

  1. 在时间 t=0,提交第一个任务以在 t=30 执行
  2. 在 t=10 时,提交第二个任务以在 t=20 时执行
  3. 在 t=15 时,将第二个任务从 t=20 推迟到 t=100
  4. t=30 到达,但第一个任务没有执行,它留在队列中。它的 getDelay 方法现在开始返回负值。
  5. t=40:第一个任务已经晚了 10 秒,仍然没有任何反应。随着时间的推移,第一个任务的 getDelay 返回越来越小的值; DelayQueue 似乎肯定是混淆的。
  6. t=90:也许是希望,因为在 q.poll 调用中设置了 30 秒的最大轮询时间。事实上,没有,我得到一个null并继续等待下一个任务;我的第一个任务仍然以负延迟留在队列中。
  7. t=100:小时!两个任务都一个接一个地执行……第二个准时,但第一个终于迟到了70秒。这是 Not Acceptable !

我还注意到,如果任务在进入队列时从未成为头,我可以安全地推迟它,即不会干扰其他任务。

所以,我的问题是:

  1. 为什么会这样?我的代码有什么问题吗?
  2. 我是否一定要删除任务,然后再次提交,以模拟推迟?还是有另一种方法可以安全地做到这一点?我真的可以删除一个对象然后重新添加完全相同的对象,还是最好提交另一个对象以确保避免所有可能的混淆?
  3. 奖励问题:删除操作的复杂性如何?大概是 O(n),如果我假设队列被实现为一种优先堆(不可能在优先堆中进行二分查找)。

感谢您的回答。

这是代码。我已经尽可能多地删除了不相关的部分。我特别删除了所有异常处理。

public abstract class AbstractTaskExecutor<R extends Runnable> implements Runnable {
private final BlockingQueue<R> q;
...
public boolean submit (R dr) { return q.add(dr); }
public void run () {
while (!Thread.currentThread().isInterrupted()) {
Runnable r = q.poll(30, TimeUnit.SECONDS);
if (r!=null) r.run();
}}
}

public abstract class DelayedRunnable implements Runnable, Delayed {
private long time;
public DelayedRunnable (long l) {
time = System.currentTimeMillis() +l;
}
public final int compareTo (Delayed d) {
return (int)( Math.min(Math.max(Integer.MIN_VALUE, time - ((DelayedRunnable)d).time), Integer.MAX_VALUE) );
}
public final long getDelay (TimeUnit t) {
return t.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
public final long getTime () { return time; }
public void postpone (long l) { time+=l; }
}


public class DelayedTaskExecutor extends AbstractTaskExecutor<DelayedRunnable> {
...
}

最佳答案

A question remains though: is it possible to reschedule the object that was just cancelled

如果您所做的只是更改“激活”的时间,则不是。您必须删除它,更改它并重新添加它,因为它可能在数据结构中的位置不同。这是必需的,因为结构决定了事件的顺序,如果您只是更改值,这可能会或可能不会导致顺序更改。如果在添加后更改 Map 的键,您会遇到类似的问题。即结构不正确。


我会使用 ScheduledExecutorService 来包装延迟队列和线程池。

当你放置一个延迟任务时,你会得到一个 Future 对象,你可以根据需要取消和重新安排。

At time t=0, submit a first task to execute at t=30

安排 30 年后的任务。

At t=10, submit a second task to execute at t=20

为 10 以后安排一个任务并保存 Future。

At t=15, postpone the second task from t=20 to t=100

future .取消和重新安排

t=30 arrive, but the first task isn't executed, it stays in the queue. Its getDelay method now starts returning negative values.

在此示例中,除非您取消它,否则它将执行。

关于java - 异步任务执行与DelayQueue : postponing an already submitted task,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14308401/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com