Я хотел, чтобы на холсте были высказаны мнения о наилучшем шаблоне проектирования для работы с управляемыми ресурсами, в котором задействованы два разных ресурса, но вам нужно освободить их в обратном порядке, чем они были приобретены.
Сначала позвольте мне установить сцену. Мы работаем с двумя типами объектов "Документы" и "Коллекции документов". Сборник документов буквально содержит ссылки на документы и некоторые метаданные для каждого документа.
Первоначально у нас был симметричный рисунок, который протекал как:
- Коллекция блокировок
- Полезный материал с коллекцией
- Заблокировать документ
- Полезный материал с коллекцией и документом
- Разблокировать документ
- Разблокировать коллекцию
и в коде был представлен как:
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();
}
}
Теперь, когда мы имеем try-with-resources
с Java 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)
}
}
Проблема заключается в том, что сохранение блокировки коллекции во время выполнения операций над документом неэффективно, так как другие потоки должны ждать, и часто операции над документом не требуют изменения коллекции.
Итак, мы хотели бы перейти к асимметричной схеме, которая позволяет нам как можно скорее выпустить коллекцию. Поток должен быть следующим:
- Коллекция блокировок
- Полезный материал с коллекцией
- Заблокировать документ
- Делайте все, что требует как 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
асимметричным управлением ресурсами разумным или нет, а также любые указатели на другие шаблоны, которые могут быть более подходящими.