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

ada*_*ter 9 java design-patterns try-with-resources

我想就使用托管资源的最佳设计模式绘制一些意见,其中涉及两个不同的资源,但您需要以与获取它们相反的顺序发布它们.

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

最初我们有一个对称的图案流动如下:

  1. 锁集合
  2. 用Collection做有用的东西
  3. 锁定文件
  4. 用Collection和Document做有用的东西
  5. 解锁文件
  6. 解锁收藏

并在代码中表示如下:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

既然我们已经拥有try-with-resourcesJava 7,那么我们已对其进行了改进,以便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)

    }

}
Run Code Online (Sandbox Code Playgroud)

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

因此,我们希望采用不对称模式,以便我们尽快发布集合.流程应该像:

  1. 锁集合
  2. 用Collection做有用的东西
  3. 锁定文件
  4. 做任何需要收集和文件的事情(罕见)
  5. 解锁收藏
  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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我还可以想到一个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
Run Code Online (Sandbox Code Playgroud)

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);
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

我欢迎有关try-with-resources非对称资源管理方法是否合理的意见,以及对其他可能更合适的模式的指示.

Dou*_*las 1

您的ManagedRelease方案确实会使代码更难以理解。使用语言特性最直接、明确地表达你的意图是这样的:

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

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

}
try (final Collection col = getCollection("col1 name", LockMode.WRITE_LOCK;
    final Document doc = col.getDocument("doc1 name", LockMode.WRITE_LOCK)) {

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

这样做的问题是每个锁的额外释放和重新获取,而且这col超出了最后一次getDocument调用的范围,因此它无法按原样编译。

我建议用不同的概念来解决这个问题ManagedRelease,提升一个层次。我设想的使用模式将如下所示:

// The lambdas here are Supplier
try (final ReleaseManager<Collection> colManager = new ReleaseManager<>(() -> getCollection("col1 name", LockMode.WRITE_LOCK);
    final ReleaseManager<Document> docManager = new ReleaseManager<>(() -> colManager.getResource().get().getDocument("doc1 name", LockMode.WRITE_LOCK)) {

    try (final Managed<Collection> colManaged = colManager.getResource()) {

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

    } // Here the resource close does nothing

    try (final Managed<Collection> colManaged = colManager.getResourceForLastUse();
        final Managed<Document> docManaged = docManager.getResource()) {

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

    } // Here the close of colManaged actually closes it, while docManaged.close() is a no-op

    try (final Managed<Document> docManaged = docManager.getResourceForLastUse()) {

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

    } // Here the document gets closed
} // Here the managers get closed, which would close their resources if needed
Run Code Online (Sandbox Code Playgroud)

这对于每个块中使用哪些资源具有相同的清晰度,使用 try-with-resources 语言功能,在上次使用后立即释放每个资源,并且仅获取每个锁一次。

对于规格ReleaseManager

ReleaseManager这是一个通用类,它采用 aSupplier作为资源,在第一次getResource()调用时延迟调用它,并记住结果以供将来调用。getResource()返回一个在关闭时不执行任何操作的包装器,getResourceForLastUse()返回一个在关闭包装器时实际上关闭资源的包装器;我将它们写为同一类,但您可以将它们改为不同的类,我不确定这是否真的使任何事情变得更清晰。

ReleaseManager它本身也实现了AutoCloseable,并且它的close()实现是一个故障安全机制,如果资源已被获取但未关闭,则会关闭资源。我会考虑让它以某种方式记录警告,以引起注意,以防资源的最后一次使用未正确声明为最后一次。最后一个考虑因素是,如果资源已关闭,则两种资源检索方法都应抛出异常。

如果您喜欢这个解决方案,我将把实现ReleaseManager作为练习留给您。