从任务本身重新提交/安排任务 - 这是一个好习惯吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从任务本身重新提交/安排任务 - 这是一个好习惯吗?相关的知识,希望对你有一定的参考价值。

考虑我们有一个预定的执行者服务:

ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(...);

对于某些逻辑,我们想重试任务执行。以下方法对我来说似乎很有气味,但我不明白为什么:

threadPool.submit(new Runnable() {
    @Override
    public void run() {
        // ...
        if (needToBeScheduled()) {
           threadPool.schedule(this, delay, TimeUnit.MINUTES);
        } else if (needToBeResubmitted()) {
           threadPool.submit(this);
        }
    }
});

我看到的一个明显问题是此代码无法转换为lambda:

threadPool.submit(()-> {
    // ...
    if (needToBeScheduled()) {
        threadPool.schedule(this, delay, TimeUnit.MINUTES);
    } else if (needToBeResubmitted()) {
        threadPool.submit(this);
    }
});

^^这将无法编译,因为我们无法从lambda引用this。虽然它可以通过引入一个生成这样一个实例的方法来解决,而不是使用this

但这只是我看到的一个缺点。这里还有什么可以导致任何问题吗?也许有更合适的方法?将此逻辑移至ThreadPoolExecutor.afterExecute()(这导致类型转换虽然......)?

假设该对象是无状态的,即Runnable实例中没有对象变量。

附:做什么(重新安排任务或重新提交或什么都不做)的逻辑基于从数据库(或任何外部源)检索的一些信息。所以Runnable仍然是无国籍的,但它根据其工作的一些结果计算结果。

答案

老实说,我不喜欢任务(一个简单的独立工作单元)决定是否应该将自己置于服务中并直接与ExecutorService交互的方法。我相信// ...是任务应该执行的唯一部分。

我会在Runnable转换Callable<Boolean>

Callable<Boolean> task = () -> {
    // ...
    return needToBeScheduled; // or sth more complex with several boolean fields
};

我肯定会将该逻辑移到任务之外(例如,转换为服务方法):

Future<Boolean> future = threadPool.submit(task);

try {
    boolean needToBeScheduled = future.get();

    if (needToBeScheduled) {
        threadPool.schedule(task, delay, TimeUnit.MINUTES);
    }
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

通过更复杂的东西,我的意思是一个包含2个boolean字段的类。需要Supplier<Boolean>s让事情变得懒散。

final class TaskResult {
    private final Supplier<Boolean> needToBeScheduled;
    private final Supplier<Boolean> needToBeResubmitted;

    private TaskResult(Supplier<Boolean> needToBeScheduled, Supplier<Boolean> needToBeResubmitted) {
        this.needToBeScheduled = needToBeScheduled;
        this.needToBeResubmitted = needToBeResubmitted;
    }

    public static TaskResult of(Supplier<Boolean> needToBeScheduled, Supplier<Boolean> needToBeResubmitted) {
        return new TaskResult(needToBeScheduled, needToBeResubmitted);
    }

    public boolean needToBeScheduled() {
        return needToBeScheduled != null && needToBeScheduled.get();
    }

    public boolean needToBeResubmitted() {
        return needToBeResubmitted != null && needToBeResubmitted.get();
    }
}

通过对上述示例进行一些更改,我们得到:

Callable<TaskResult> task = () -> {
    // ...
    return TaskResult.of(() -> needToBeScheduled(), () -> needToBeResubmitted());
};

final Future<TaskResult> future = threadPool.submit(task);

try {
    final TaskResult result = future.get();

    if (result.needToBeScheduled()) {
        threadPool.schedule(task, delay, TimeUnit.MINUTES);
    }

    if (result.needToBeResubmitted()) {
        threadPool.submit(task);
    }
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

以上是关于从任务本身重新提交/安排任务 - 这是一个好习惯吗?的主要内容,如果未能解决你的问题,请参考以下文章

在每个请求之前重新加载凭据是一个好习惯吗?

如何安排脚本定期运行?

如果任务抛出 RuntimeException/Error,ScheduledExecutorService.scheduleAt* 方法是不是应该重新安排任务?

Django q 更新一个已经安排好的任务

在 Kafka 流作业中进行同步数据库查询或 restful 调用是一种好习惯吗?

进程里有个schtasks.exe.这是个啥东西啊?可以删除吗?