【问题标题】:ScheduledExecutorService with variable delay具有可变延迟的 ScheduledExecutorService
【发布时间】:2010-12-03 21:29:32
【问题描述】:

假设我有一个任务是从 java.util.concurrent.BlockingQueue 中提取元素并处理它们。

public void scheduleTask(int delay, TimeUnit timeUnit)
{
    scheduledExecutorService.scheduleWithFixedDelay(new Task(queue), 0, delay, timeUnit);
}

如果可以动态更改频率,我如何安排/重新安排任务?

  • 我们的想法是获取数据更新流并将它们批量传播到 GUI
  • 用户应该能够改变更新频率

【问题讨论】:

  • 我不清楚您为什么要使用阻塞队列。如果你的队列是空的。我假设您的计划任务将被阻止。这是你的意图吗?这可能会混淆任务调度程序的计时。
  • 我选择了 ArrayBlockingQueue 实现,因为它必须是线程安全的、尊重 FIFO 排序和有界的。就算任务阻塞了,也不应该混淆任务调度吧?
  • 您使用 BlockingQueue 实现是正确的(实际上 ScheduledThreadPoolExecutor 在内部使用了一个)。但是,您为什么要使用计时器将更新传播到 GUI?为什么不实时进行呢?更新太多了吗?您是否担心 Swing 线程旋转?
  • 我想批量更新。所以在内部,事件可能每毫秒到达一次,但 gui 会每 100 毫秒刷新一次。我认为这在视觉上会更具吸引力,并且 CPU 开销也会更少。我使用的是 Eclipse RCP 而不是 Swing。

标签: java concurrency executorservice blockingqueue


【解决方案1】:

如果您尝试以特定间隔处理多个队列任务,您不应该使用scheduleAtFixedRate 吗? scheduleWithFixedDelay 只会等待指定的延迟,然后执行队列中的一项任务。

在任何一种情况下,ScheduledExecutorService 中的 schedule* 方法都将返回 ScheduledFuture 引用。如果您想更改费率,您可以取消ScheduledFuture 并以不同的费率重新安排任务。

【讨论】:

  • scheduleWithFixedDelay(...) - 创建并执行一个周期性操作,该操作在给定的初始延迟之后首先启用,随后在一个执行的终止和下一个执行的开始之间具有给定的延迟。如果任务的任何执行遇到异常,则后续执行将被抑制。否则,任务只会通过取消或终止执行者来终止。
  • 能否给出取消和重新安排的代码示例?正在进行的更新呢?
【解决方案2】:

我认为您无法更改固定速率延迟。我认为您需要使用schedule() 执行一次性操作,并在完成后再次安排(如果需要,可以修改超时)。

【讨论】:

  • 谢谢 - 我将“延迟”作为一个实例变量并添加了一个私有方法来执行以下操作:while(!executorService.isShutdown) { executorService.schedule(new Task(queue), delay, TimeUnit.毫秒); }
【解决方案3】:

scheduleWithFixedDelay(...) 返回一个 RunnableScheduledFuture。为了重新安排它,您可以取消并重新安排它。要重新安排它,你可以只用一个新的 Runnable 包装 RunnableScheduledFuture:

new Runnable() {
    public void run() {
        ((RunnableScheduledFuture)future).run();
    }
};

【讨论】:

    【解决方案4】:

    使用schedule(Callable<V>, long, TimeUnit) 而不是scheduleAtFixedRatescheduleWithFixedDelay。然后确保您的 Callable 在未来的某个时间重新安排自身或新的 Callable 实例。例如:

    // Create Callable instance to schedule.
    Callable<Void> c = new Callable<Void>() {
      public Void call() {
       try { 
         // Do work.
       } finally {
         // Reschedule in new Callable, typically with a delay based on the result
         // of this Callable.  In this example the Callable is stateless so we
         // simply reschedule passing a reference to this.
         service.schedule(this, 5000L, TimeUnit.MILLISECONDS);
       }  
       return null;
      }
    }
    
    service.schedule(c);
    

    这种方法无需关闭并重新创建ScheduledExecutorService

    【讨论】:

    • 您可以(应该?)使用Runnable,而不是Callable&lt;Void&gt;
    • 我没有找到 ScheduledExecutorService 的 schedule(Callable) 函数。只有具有所有参数的那些。你能指点我在哪里吗?或者至少将示例修复为包含 0 的延迟。
    【解决方案5】:

    我最近不得不使用 ScheduledFuture 执行此操作,并且不想包装 Runnable 等。我是这样做的:

    private ScheduledExecutorService scheduleExecutor;
    private ScheduledFuture<?> scheduleManager;
    private Runnable timeTask;
    
    public void changeScheduleTime(int timeSeconds){
        //change to hourly update
        if (scheduleManager!= null)
        {
            scheduleManager.cancel(true);
        }
        scheduleManager = scheduleExecutor.scheduleAtFixedRate(timeTask, timeSeconds, timeSeconds, TimeUnit.SECONDS);
    }
    
    public void someInitMethod() {
    
        scheduleExecutor = Executors.newScheduledThreadPool(1);    
        timeTask = new Runnable() {
            public void run() {
                //task code here
                //then check if we need to update task time
                if(checkBoxHour.isChecked()){
                    changeScheduleTime(3600);
                }
            }
        };
    
        //instantiate with default time
        scheduleManager = scheduleExecutor.scheduleAtFixedRate(timeTask, 60, 60, TimeUnit.SECONDS);
    }
    

    【讨论】:

      猜你喜欢
      • 2023-01-21
      • 1970-01-01
      • 2017-03-28
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 2018-04-23
      相关资源
      最近更新 更多