java - 管理不对称资源使用的最佳设计模式

标签 java design-patterns try-with-resources

我想就使用托管资源的最佳设计模式征求一些意见,其中涉及两种不同的资源,但您需要以与获取它们的顺序相反的顺序释放它们。

首先,让我设置一下场景。我们正在使用两种类型的对象文档和文档集合。文档集合字面上包含对文档的引用和每个文档的一些元数据。

最初我们有一个对称的模式,它像这样流动:

  1. 锁定 Collection
  2. 使用 Collection 做有用的事情
  3. 锁定文件
  4. 使用 Collection 和 Document 做有用的事情
  5. 解锁文件
  6. 解锁 Collection

在代码中表示为:

Collection col = null;
try {
    col = getCollection("col1 name", LockMode.WRITE_LOCK);

    // Here we do any operations that only require the Collection

    Document doc = null;
    try {
        doc = col.getDocument("doc1 name", LockMode.WRITE_LOCK);

        // Here we do some operations on the document (of the Collection)

    } finally {
        if (doc != null) {
            doc.close();
        }
    }

} finally {
    if (col != null) {
        col.close();
    }
}

自从 Java 7 以来我们有了 try-with-resources,我们对此进行了改进,以便 Java 代码描述自动释放资源:

try (final Collection col = getCollection("col1 name", LockMode.WRITE_LOCK)) {

    // Here we do any operations that only require the Collection

    try (final Document doc = col.getDocument("doc1 name", LockMode.WRITE_LOCK)) {

        // Here we do some operations on the document (of the Collection)

    }

}

我们遇到的问题是,在我们对文档执行操作时保持 Collection 锁定是低效的,因为其他线程必须等待,而且通常对文档的操作不需要修改 Collection。

因此,我们希望采用非对称模式,以便我们尽快发布该系列。流程应该是这样的:

  1. 锁定 Collection
  2. 使用 Collection 做有用的事情
  3. 锁定文件
  4. 做任何需要集合和文档的事情(很少见)
  5. 解锁 Collection
  6. 使用 Document 做有用的事情
  7. 解锁文档

我想知道在代码中实现这种不对称方法的最佳模式。这显然可以像这样用 try/finally 等来完成:

Collection col = null;
Document doc = null;
try {
    col = getCollection("col1 name", LockMode.WRITE_LOCK);

    // Here we do any operations that only require the Collection
    try {
        doc = col.getDocument("doc1 name", LockMode.WRITE_LOCK);

        // Here we do any operations that require both the Collection and Document (rare).

    } finally {
        if (col != null) {
        col.close();
    }

    // Here we do some operations on the document (of the Collection)

} finally {
    if (doc != null) {
            doc.close();
        }
    }
}

我还可以想到一个 try-with-resources 方案,我们在其中交换资源释放顺序,但我想知道这是否会使阅读代码变得更难理解。例如:

try (final ManagedRelease<Collection> mcol =
        new ManagedRelease<>(getCollection("col1 name", LockMode.WRITE_LOCK))) {

    // Here we do any operations that only require the Collection

    try (final ManagedRelease<Document> mdoc =
            mcol.withAsymetrical(mcol.resource.getDocument("doc1 name", LockMode.WRITE_LOCK))) {

        // Here we do any operations that require both the Collection and Document (rare).

    }  // NOTE: Collection is released here

    // Here we do some operations on the document (of the Collection)

}  // NOTE: Document is released here

ManagedRelease 类:

private static class ManagedRelease<T extends AutoCloseable> implements AutoCloseable {
    final T resource;
    private Supplier<Optional<Exception>> closer;

    public ManagedRelease(final T resource) {
        this.resource = resource;
        this.closer = asCloserFn(resource);
    }

    private ManagedRelease(final T resource, final Supplier<Optional<Exception>> closer) {
        this.resource = resource;
        this.closer = closer;
    }

    public <U extends AutoCloseable> ManagedRelease<U> withAsymetrical(final U otherResource) {
        // switch the closers of ManagedRelease<T> and ManagedRelease<U>
        final ManagedRelease<U> asymManagedResource = new ManagedRelease<>(otherResource, closer);
        this.closer = asCloserFn(otherResource);
        return asymManagedResource;
    }

    @Override
    public void close() throws Exception {
        final Optional<Exception> maybeEx = closer.get();
        if(maybeEx.isPresent()) {
            throw maybeEx.get();
        }
    }

    private static Supplier<Optional<Exception>> asCloserFn(final AutoCloseable autoCloseable) {
        return () -> {
            try {
                autoCloseable.close();
                return Optional.empty();
            } catch (final Exception e) {
                return Optional.of(e);
            }
        };
    }
}

我欢迎就非对称资源管理的 try-with-resources 方法是否明智提出意见,以及任何指向可能更合适的其他模式的指针。

最佳答案

第一个问题似乎是未指定的预期行为。特别是,如果 Collection.close 抛出 Exception,应该发生什么? Document 处理应该继续吗?是否应该回滚在两个锁下完成的部分文档处理?

如果答案是 Collection.close 实际上从未抛出任何异常(或者您不关心抛出异常会发生什么),恕我直言,最简单的解决方案是让您的 Collection.close 幂等,然后在 try-with-resources block 中间适当的地方显式调用它。如果在关闭的 Collection 上调用,使“常规”Collection 方法引发类似 IllegalStateException 的东西也很有意义。然后你的第二个例子会变成这样:

try (final Collection col = getCollection("col1 name", LockMode.WRITE_LOCK)) {
    // Here we do any operations that only require the Collection

    try (final Document doc = col.getDocument("doc1 name", LockMode.WRITE_LOCK)) {

        // Here we do any operations that require both the Collection and Document (rare).


        // NOTE: usually Collection is released here
        col.close();
        // optionally make `col` not final and explicitly set it to `null`
        // here so IDE would notify you about any usage after this point

        // Here we do some operations on the document (of the Collection)

    }  
}  

如果您不能更改Collection.close 代码,您可以更改您的ReleaseManager 以使close 幂等。您也可以选择将其重命名为 ResourceManager 之类的名称。在那里添加一个 getter 并始终仅通过该 getter 访问资源。如果在 close 之后调用,getter 将抛出 IllegalStateException

如果 Collection.close 实际上可能会抛出一些异常,而您确实关心此类情况,则很难在不知道预期行为是什么的情况下提供解决方案。

关于java - 管理不对称资源使用的最佳设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46864834/

相关文章:

Java监听器实现

javascript - 将 javascript 放在 header 之外有多糟糕?

java - 对 CloseableHttpClient 使用 try-with-resource block 是否也会关闭返回的 CloseableHttpResponse?

java - 在 Java Apache Derby 数据库中获取 java.sql.SQLSyntaxErrorException : Syntax error: Encountered "(" at line 1, 第 53 列?

java - 为 Jpa 存储库设置 sql_mode=(SELECT REPLACE(@@sql_mode ,'ONLY_FULL_GROUP_BY' ,'' ))

java - apache poi 最大行数

c# - 在 C# 中使用 Moq 模拟服务

java - 为什么ExecutorService接口(interface)没有实现AutoCloseable?

Java Try With Resources 不适用于赋值?

java - 添加组件时 JPanel 或他的任何方法都不起作用