ada*_*ter 9 java design-patterns try-with-resources
我想就使用托管资源的最佳设计模式绘制一些意见,其中涉及两个不同的资源,但您需要以与获取它们相反的顺序发布它们.
首先,让我设置场景.我们正在使用两种类型的对象文档和文档集合.文档集合字面上包含对文档的引用和每个文档的一些元数据.
最初我们有一个对称的图案流动如下:
并在代码中表示如下:
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.
因此,我们希望采用不对称模式,以便我们尽快发布集合.流程应该像:
我想知道在代码中实现这种不对称方法的最佳模式.这显然可以通过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非对称资源管理方法是否合理的意见,以及对其他可能更合适的模式的指示.
您的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作为练习留给您。
| 归档时间: |
|
| 查看次数: |
523 次 |
| 最近记录: |