В библиотеке Java 7 у меня есть класс, который предоставляет сервисы другим классам. После создания экземпляра этого класса обслуживания один его метод может вызываться несколько раз (назовем его doWork()
методом). Поэтому я не знаю, когда работа класса обслуживания будет завершена.
Проблема в том, что класс обслуживания использует тяжелые объекты и должен их освобождать. Я установил эту часть в методе (давайте назовем его release()
), но не гарантируется, что другие разработчики будут использовать этот метод.
Есть ли способ заставить других разработчиков вызывать этот метод после выполнения задачи класса обслуживания? Конечно, я могу это задокументировать, но я хочу заставить их.
Примечание: я не могу вызвать release()
метод в doWork()
методе, потому что doWork()
нуждается в тех объектах, когда он вызывается в следующем.
AutoCloseable
был сделан именно для этой цели.Ответы:
Прагматичное решение состоит в том, чтобы создать класс
AutoCloseable
и предоставитьfinalize()
метод в качестве поддержки (если это уместно ... см. Ниже!). Затем вы полагаетесь на то, что пользователи вашего класса будут использовать try-with-resource илиclose()
явно вызывать .К сожалению, нет никакого способа 1 в Java , чтобы заставить программиста сделать правильную вещь. Лучшее, на что вы можете надеяться, это обнаружить неправильное использование в статическом анализаторе кода.
По теме финализаторов. Эта функция Java имеет очень мало хороших вариантов использования. Если вы упорядочиваете финализаторы, вы сталкиваетесь с проблемой того, что приведение в порядок может занять очень много времени. Финализатор будет запущен только после того, как ГХ решит, что объект больше недоступен. Этого может не произойти, пока JVM не выполнит полную коллекцию.
Таким образом, если проблема, которую вы пытаетесь решить, состоит в том, чтобы вернуть ресурсы, которые должны быть выпущены раньше , то вы пропустили лодку, используя финализацию.
И на тот случай, если вы не поняли того, что я говорю выше ... почти никогда не целесообразно использовать финализаторы в производственном коде, и вы никогда не должны полагаться на них!
1 - Есть способы. Если вы готовы «спрятать» объекты службы от кода пользователя или жестко контролировать их жизненный цикл (например, https://softwareengineering.stackexchange.com/a/345100/172 ), тогда код пользователя не должен вызывать
release()
. Тем не менее, API становится более сложным, ограниченным ... и "некрасивым" IMO. Кроме того, это не заставляет программиста поступать правильно . Это устраняет способность программиста делать неправильные вещи!источник
DefaultResource
иFinalizableResource
которая проходит первый и добавляет финализации. В производстве вы используете,DefaultResource
который не имеет финализатор-> нет накладных расходов. Во время разработки и тестирования вы настраиваете приложение для использования,FinalizableResource
которое имеет финализатор и, следовательно, добавляет накладные расходы. Во время разработки и тестирования это не проблема, но она может помочь вам выявить утечки и устранить их, прежде чем приступить к работе. Тем не менее, если утечка достигает PRD, вы можете настроить фабрику для создания X%,FinalizableResource
чтобы идентифицировать утечкуЭто похоже на сценарий использования
AutoCloseable
интерфейса иtry-with-resources
оператор, введенный в Java 7. Если у вас есть что-то вродетогда ваши потребители могут использовать его как
и не нужно беспокоиться о вызове явного
release
независимо от того, вызывает ли их код исключение.Дополнительный ответ
Если вы действительно хотите, чтобы никто не мог забыть использовать вашу функцию, вам нужно сделать пару вещей.
MyService
являются частными.MyService
которая обеспечит ее очистку после.Вы бы сделали что-то вроде
Вы бы тогда использовали его как
Первоначально я не рекомендовал этот путь, потому что он отвратителен без лямбда-выражений Java-8 и функциональных интерфейсов (где это
MyServiceConsumer
делаетсяConsumer<MyService>
), и это довольно навязывает потребителям. Но если то, что вы хотите, это то, что вамrelease()
нужно позвонить, это сработает.источник
try-with-resources
а не просто пропускатьtry
, тем самым пропускаяclose
вызов?try-with-resources
?.close()
вызывается при реализации классаAutoCloseable
.using
блоком для детерминированной финализации? По крайней мере, в C # это становится довольно четким шаблоном для разработчиков, использующих ресурсы, требующие детерминированной доработки. Нечто легкое и формирование привычки - следующая лучшая вещь, когда вы не можете заставить кого-то что-то делать. stackoverflow.com/a/2016311/84206Да, ты можешь
Вы можете абсолютно заставить их выпустить и сделать так, чтобы их было на 100% невозможно забыть, лишив их возможности создавать новые
Service
экземпляры.Если они сделали что-то подобное раньше:
Затем измените его, чтобы они могли получить к нему доступ таким образом.
Предостережения:
AutoCloseable
интерфейс, который кто-то упоминал; но данный пример должен работать «из коробки», и кроме элегантности (которая сама по себе является хорошей целью) не должно быть никаких серьезных причин для ее изменения. Обратите внимание, что это означает, что вы можете использоватьAutoCloseable
внутри вашей частной реализации; Преимущество моего решения перед использованием ваших пользователейAutoCloseable
заключается в том, что оно, опять же, не может быть забыто.new Service
вызов.Service
) в руки вызывающей стороны или нет, выходит за рамки этого ответа. Но если вы решите, что вам это абсолютно необходимо, это то, как вы можете это сделать.источник
Вы можете попытаться использовать шаблон Command .
Это дает вам полный контроль над приобретением и выпуском ресурсов. Вызывающий абонент только отправляет задачи в вашу Службу, а вы сами несете ответственность за приобретение и очистку ресурсов.
Однако есть одна загвоздка. Абонент все еще может сделать это:
Но вы можете решить эту проблему, когда она возникнет. Когда возникают проблемы с производительностью, и вы обнаруживаете, что некоторые абоненты делают это, просто покажите им правильный путь и решите проблему:
Вы можете сделать это сколь угодно сложным, выполняя обещания для задач, которые зависят от других задач, но затем выпуск вещей также становится более сложным. Это только отправная точка.
асинхронный
Как было отмечено в комментариях, вышеприведенное не работает с асинхронными запросами. Правильно. Но это легко может быть решено в Java 8 с использованием CompleteableFuture , особенно
CompleteableFuture#supplyAsync
для создания отдельных вариантов будущего иCompleteableFuture#allOf
для освобождения ресурсов после завершения всех задач. В качестве альтернативы, всегда можно использовать Threads или ExecutorServices для развертывания собственной реализации Futures / Promises.источник
Если вы можете, не позволяйте пользователю создавать ресурс. Позвольте пользователю передать объект посетителя вашему методу, который создаст ресурс, передаст его методу объекта посетителя и впоследствии освободит его.
источник
AutoCloseable
делает очень похожую вещь за кадром. Под другим углом это похоже на внедрение зависимости .Моя идея была похожа на идею Полигнома.
Вы можете использовать методы do_something () в своем классе, просто добавляя команды в личный список команд. Затем используйте метод commit (), который фактически выполняет эту работу и вызывает метод release ().
Таким образом, если пользователь никогда не вызывает commit (), работа никогда не завершается. Если они это сделают, ресурсы будут освобождены.
Затем вы можете использовать его как foo.doSomething.doSomethineElse (). Commit ();
источник
Другой альтернативой упомянутым может быть использование «умных ссылок» для управления инкапсулированными ресурсами таким образом, чтобы сборщик мусора автоматически очищался без освобождения ресурсов вручную. Их полезность зависит, конечно, от вашей реализации. Подробнее об этой теме читайте в этом замечательном сообщении в блоге: https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references
источник
Для любого другого языка, ориентированного на класс, я бы сказал, поместите его в деструктор, но, поскольку это не вариант, я думаю, вы можете переопределить метод finalize. Таким образом, когда экземпляр выходит из области видимости и собирается мусором, он будет освобожден.
Я не слишком знаком с Java, но я думаю, что это цель, для которой предназначена finalize ().
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#finalize ()
источник
If you rely on finalizers to tidy up, you run into the problem that it can take a very long time for the tidy-up to happen. The finalizer will only be run after the GC decides that the object is no longer reachable. That may not happen until the JVM does a full collection.