当我们允许注入时如何管理 ExecutorService 的关闭?

How to manage shutdown of ExecutorService when we allow to inject it?

假设我正在编写一个服务,它需要一些执行程序 service/separate 线程。 我提供了使用工厂方法的能力,不用担心执行器服务,但仍然希望允许传递现有的执行器服务(依赖注入)。

如何管理 executorService.shutdown()

示例代码:

public class ThingsScheduler {

    private final ExecutorService executorService;

    public ThingsScheduler(ExecutorService executorService) {
        this.executorService = executorService;
    }

    public static ThingsScheduler createDefaultSingleThreaded() {
        return new ThingsScheduler(Executors.newSingleThreadExecutor());
    }

    public scheduleThing() {
        executorService.submit(new SomeTask());
    }

    // implement Closeable?
    // @PreDestory?
    // .shutdown() + JavaDoc?
}

有几个问题

我们可以创建一些属性来说明 executor 是由我们的 class 创建的还是被注入的,然后在 finalize/@PreDestroy/shutdown 挂钩上我们可以关闭它,但感觉并不优雅对我来说。

也许我们应该完全放弃工厂方法,总是要求注入将执行器生命周期管理推给客户端?

我会说这个解决方案完全取决于您。 spring 等第三方库广泛使用专用属性来了解谁应该根据其创建者发布特定资源。 mongoInstanceCreatedSimpleMongoDbFactory, localServer in SimpleHttpServerJaxWsServiceExporter 等中。但他们这样做是因为这些 classes 仅供外部使用。如果您的 class 仅在您的应用程序代码中使用,那么您可以注入 executorService 并且不关心它的释放或在使用它的 class 中创建和释放它。这个选择取决于您的 class/application 设计(您的 class 是否与任何 executorService 一起工作,executorService 是否被其他 class 共享和使用,等等)。否则我看不到除专用标志之外的其他选项。

更多 "elegant" 解决方案是扩展您的 ExecutorService 并在其中覆盖关闭方法(无论您选择哪个)。在注入的情况下,您将 return 那个扩展类型,它会有自己的关闭逻辑。如果是工厂 - 你仍然有原始逻辑。

您可以从您的默认工厂创建一个匿名子内部 class 的实例,如下所示。 class 将定义 close/@PreDestroy 方法,您的 DI 容器将调用该方法。 例如

public class ThingsScheduler {
    final ExecutorService executorService;

    public ThingsScheduler(ExecutorService executorService) {
        this.executorService = executorService;
    }

    /**
     * assuming you are using this method as factory method to make the returned
     * bean as managed by your DI container
     */
    public static ThingsScheduler createDefaultSingleThreaded() {
        return new ThingsScheduler(Executors.newSingleThreadExecutor()) {
            @PreDestroy
            public void close() {
                System.out.println("closing the bean");
                executorService.shutdown();
            }
        };
    }
}

经过一番思考,我得出了一些结论:

  • 如果它被注入,请不要考虑将其关闭 - 其他人创建它,其他人将管理它的生命周期
  • 一个执行器工厂可以被注入而不是执行器,然后我们使用工厂创建实例并在我们管理生命周期时自行关闭它(在这种情况下来自其他响应用户申请)