我想就使用托管资源的最佳设计模式征求一些意见,其中涉及两种不同的资源,但您需要以与获取它们的顺序相反的顺序释放它们。
首先,让我设置一下场景。我们正在使用两种类型的对象文档和文档集合。文档集合字面上包含对文档的引用和每个文档的一些元数据。
最初我们有一个对称的模式,它像这样流动:
- 锁定 Collection
- 使用 Collection 做有用的事情
- 锁定文件
- 使用 Collection 和 Document 做有用的事情
- 解锁文件
- 解锁 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。
因此,我们希望采用非对称模式,以便我们尽快发布该系列。流程应该是这样的:
- 锁定 Collection
- 使用 Collection 做有用的事情
- 锁定文件
- 做任何需要集合和文档的事情(很少见)
- 解锁 Collection
- 使用 Document 做有用的事情
- 解锁文档
我想知道在代码中实现这种不对称方法的最佳模式。这显然可以像这样用 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/