Причина вызова shutdown () на ExecutorService

84

Я читал об этом совсем немного в последние пару часов, и я просто не вижу никаких оснований ( действительна причина) для вызова shutdown()на ExecutorService, если мы не будем иметь Humongous приложение , которое хранит, десятки и десятки различных услуг ИСПОЛНИТЕЛЬ, которые не используются для долго.

Единственное (из того, что я понял) завершение работы - это делать то, что делает обычный поток после завершения. Когда обычный поток завершит выполнение метода Runnable (или Callable), он будет передан в сборку мусора для сбора. С Executor Service потоки будут просто приостановлены, они не будут отмечены для сборки мусора. Для этого нужно отключение.

Хорошо, вернемся к моему вопросу. Есть ли причина вызывать выключение ExecutorServiceочень часто или даже сразу после отправки ему некоторых задач? Я хотел бы оставить случай, когда кто-то это делает, и сразу после этого звонит, awaitTermination()когда это подтверждается. Как только мы это сделаем, нам придется заново воссоздавать новое ExecutorService, чтобы сделать то же самое. Разве не идея ExecutorServiceповторного использования потоков? Так зачем уничтожать ExecutorServiceтак быстро?

Разве это не рациональный способ просто создать ExecutorService(или объединить, в зависимости от того, сколько вам нужно), затем во время работы приложения передать им задачи, как только они появятся, а затем при выходе из приложения или на некоторых других важных этапах выключить эти исполнители ?

Я хотел бы получить ответ от опытных программистов, которые пишут много асинхронного кода с помощью ExecutorServices.

Второй побочный вопрос, немного поменьше, касается платформы Android. ЕСЛИ некоторые из вас скажут, что отключать исполнителей каждый раз - не лучшая идея, и вы программируете на Android, не могли бы вы рассказать мне, как вы справляетесь с этими отключениями (чтобы быть конкретными - когда вы их выполняете), когда мы имеем дело с различными событиями жизненный цикл приложения.

Из-за комментария CommonsWare я сделал сообщение нейтральным. Мне действительно неинтересно спорить об этом до смерти, и, похоже, это ведет к этому. Мне интересно узнать о том, о чем я здесь просил, опытных разработчиков, только если они готовы поделиться своим опытом. Благодарю.

Лукас
источник
3
«Я много раз видел примеры кодов, в которых всегда есть вызов shutdown () сразу после отправки или выполнения задачи» - не стесняйтесь использовать гиперссылки для подтверждения своих требований. Лично я никогда не видел никаких «примеров кодов», которые делали бы то, что вы заявляете. Возможно, вы что-то неправильно интерпретируете, и мы можем указать вам на это, только если знаем, какие «образцы кодов» вы исследуете.
CommonsWare
4
Привет CommonsWare. Прежде всего, я вижу ваш агрессивный тон (или так кажется) в отношении меня, который, я думаю, здесь не подтверждается. Я не пытался изображать людей негативным образом. Что касается вашей цитаты, я в основном говорил о выпуске Thinking In Java IV, многозадачности. Вы можете найти множество примеров этого в примерах Брюса Экеля. В основном они просты, но, тем не менее, Брюс произвел на меня впечатление, что отключение выполняется очень часто. В любом случае, вы сосредоточились на том, что не было основной частью моего поста. Я удалил эти части, потому что не хочу спорить об этом.
Лукас
1
hay @CommonsWare in Thinking in Java book Брюса Экеля. на странице «Параллелизм / Исполнитель», страница 804, четвертое издание, он всегда использует метод shutdown () сразу после отправки или выполнения задач в простых приложениях, чтобы проиллюстрировать, как работает «Исполнитель», как сказал Лукас
ошибка
2
Я знаю, что это старый пост, но я думаю, что вопрос OP все еще остается в силе. Я также встречал много примеров кода, где «сразу после выполнения () выполняется вызов shutdown ()». tutorials.jenkov.com/java-util-concurrent/executorservice.html (первое руководство, которое появляется, когда вы гуглите «пример java executorservice»)
baekacaek
Спасибо, у меня возник тот же вопрос, что и с этими «примерами кодов». journaldev.com/2340/…
Грегорди

Ответы:

57

shutdown()Метод делает одну вещь: не позволяет клиентам отправлять больше работы службы исполнителя. Это означает, что все существующие задачи по-прежнему будут выполняться до завершения, если не будут предприняты другие действия. Это верно даже для запланированных задач, например, для ScheduledExecutorService: новые экземпляры запланированной задачи не запускаются. Это может быть полезно в различных сценариях.

Предположим, у вас есть консольное приложение, в котором есть служба-исполнитель, выполняющая N задач. Если пользователь нажимает CTRL-C, вы ожидаете, что приложение завершится, возможно, изящно. Что это значит изящно? Возможно, вы хотите, чтобы ваше приложение не могло отправлять больше задач в службу исполнителя, и в то же время вы хотите дождаться завершения существующих N задач. Вы можете добиться этого, используя в крайнем случае ловушку выключения:

final ExecutorService service = ... // get it somewhere

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Performing some shutdown cleanup...");
        service.shutdown();
        while (true) {
            try {
                System.out.println("Waiting for the service to terminate...");
                if (service.awaitTermination(5, TimeUnit.SECONDS)) {
                    break;
                }
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Done cleaning");
    }
}));

Этот перехватчик завершит работу службы, что не позволит вашему приложению отправлять новые задачи, и дождаться завершения всех существующих задач, прежде чем закрывать JVM. Завершение ожидания будет заблокировано на 5 секунд и вернет истину, если служба отключена. Это выполняется в цикле, чтобы вы были уверены, что служба в конечном итоге отключится. InterruptedException проглатывается каждый раз. Это лучший способ отключить службу-исполнитель, которая повторно используется во всем вашем приложении.

Этот код не идеален. Если вы не абсолютно уверены, что ваши задачи в конечном итоге завершатся, вы можете подождать заданный тайм-аут, а затем просто выйти, оставив работающие потоки. В этом случае имеет смысл также вызвать shutdownNow()после тайм-аута в последней попытке прервать запущенные потоки ( shutdownNow()также будет предоставлен список задач, ожидающих выполнения). Если ваши задачи предназначены для реагирования на прерывания, это будет работать нормально.

Другой интересный сценарий - это когда у вас есть ScheduledExecutorService, который выполняет периодическую задачу. Единственный способ остановить цепочку периодических задач - позвонить shutdown().

РЕДАКТИРОВАТЬ: Я хотел бы добавить, что я бы не рекомендовал использовать крючок выключения, как показано выше, в общем случае: он может быть подвержен ошибкам и должен использоваться только в крайнем случае. Более того, если у вас зарегистрировано много обработчиков выключения, порядок их запуска не определен, что может быть нежелательным. Я предпочел бы иметь приложение явно вызвать shutdown()на InterruptedException.

Джованни Ботта
источник
Извини, Джованни за запоздалый ответ, и спасибо тебе за это, кстати. Да, я знаю, как работает Executor, что я пытался объяснить в своем вопросе. Завершение работы делает то, что вы сказали, а также позволяет сборщику мусора собирать эти мертвые потоки и, по сути, собирать экземпляр ExecutorService. Мой вопрос был конкретным. Есть ли причина постоянно вызывать «shutdown ()» сразу после того, как вы отправите / выполните что-либо на ExecutorService. Вторая часть вопроса касается строго архитектуры Android. Если ответ на предыдущий отрицательный, то когда вызывать выключение во время и. жизненный цикл.
Лукас
3
Нет причин постоянно вызывать shutdown (). Фактически, это может быть абсолютно неправильным поступком, поскольку это не позволит вам повторно использовать службу исполнителя снова. Причина вызова его в конце жизненного цикла службы состоит в том, чтобы, как вы заметили, потоки, наконец, могли быть окончательно собраны мусором. Если вы этого не сделаете, эти потоки будут поддерживать JVM в рабочем состоянии, даже если они простаивают.
Джованни Ботта,
«Нет причин постоянно вызывать shutdown (). Фактически, это может быть абсолютно неправильным поступком, поскольку это не позволит вам повторно использовать службу исполнителя». Это именно мои рассуждения и моя дилемма из исходного вопроса. Итак, повторимся, вопрос: когда мне следует выключить ExecutiveService в жизненном цикле Android?
Лукас
2
У меня нет опыта работы с Android, но я предполагаю, что вам следует закрыть его, когда ваше приложение завершает работу, чтобы позволить JVM наконец выйти.
Джованни Ботта,
3
Понимаю. Я предлагаю вам использовать кэшированный пул потоков и никогда не вызывать shutdown()его, чтобы не тратить ресурсы, когда они не нужны. Если приложение завершает работу, потоки в пуле в конечном итоге будут собираться мусором (по умолчанию после того, как они простаивают в течение 60 секунд). Обратите внимание: если вы хотите, чтобы пул был ограничен или вам нужно другое время жизни потока, вы можете ThreadPoolExecutorнапрямую создать файл.
Джованни Ботта
13

Разве вся идея ExecutorService не заключается в повторном использовании потоков? Так зачем же так быстро уничтожать ExecutorService?

Да. Не следует ExecutorServiceчасто разрушать и воссоздавать . Инициализируйте его, ExecutorServiceкогда вам нужно (в основном, при запуске), и держите его активным, пока вы не закончите с ним.

Разве это не рациональный способ просто создать ExecutorService (или пару, в зависимости от того, сколько вам нужно), затем во время работы приложения передать им задачи, как только они появятся, а затем при выходе из приложения или на некоторых других важных этапах выключить их исполнители?

Да. Завершение работы рационально ExecutorServiceна важных этапах, таких как выход из приложения и т. Д.

Второй побочный вопрос, немного поменьше, касается платформы Android. ЕСЛИ некоторые из вас скажут, что отключать исполнителей каждый раз - не лучшая идея, и вы программируете на Android, не могли бы вы рассказать мне, как вы справляетесь с этими отключениями (точнее, когда вы их выполняете), когда мы имеем дело с различными событиями приложения жизненный цикл.

Предположим, что ExecutorServiceон используется в разных Activity в вашем приложении. Каждое действие будет приостанавливаться / возобновляться через разные промежутки времени, и вам все равно понадобится одно для ExecutorServiceкаждого приложения.

Вместо того, чтобы управлять состоянием ExecutorServiceв методах жизненного цикла Activity, переместите управление ExecutorService (создание / завершение работы) в свою настраиваемую службу .

Создать ExecutorServiceв Service => onCreate()и правильно выключить вonDestroy()

Рекомендуемый способ выключения ExecutorService:

Как правильно завершить работу java ExecutorService

Равиндра бабу
источник
3

ExecutorService должен быть отключен, как только он больше не нужен, чтобы освободить системные ресурсы и обеспечить плавное завершение работы приложения. Поскольку потоки в ExecutorService могут быть ненадежными потоками, они могут препятствовать нормальному завершению работы приложения. Другими словами, ваше приложение продолжает работать после завершения своего основного метода.

Справочник

Глава: 14 Страница: 814

эмон
источник
0

Причина вызова shutdown () на ExecutorService

Сегодня я столкнулся с ситуацией, когда мне нужно подождать, пока машина будет готова, прежде чем начать серию задач на этой машине.

Я делаю REST-вызов на эту машину, если я не получаю 503 (Сервер недоступен), значит, машина готова обрабатывать мои запросы. Итак, я жду, пока не получу 200 (Успех) для первого вызова REST.

Есть несколько способов добиться этого, я использовал ExecutorService для создания потока и запланировал его запуск через каждые X секунд. Итак, мне нужно остановить этот поток при условии, проверьте это ...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> {
        try {
            int statusCode = restHelper.firstRESTCall();

            if (statusCode == 200) {
                executor.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    int retryAfter = 60;
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS);

Второй побочный вопрос, немного поменьше, касается платформы Android.

Может быть, я смогу ответить, если вы предоставите немного больше контекста! Кроме того, по моему опыту разработки под Android, потоки нужны редко. Вы разрабатываете игру или приложение, которому для производительности нужны потоки? Если нет, то в Android у вас есть другие способы решения проблем, такие как сценарий, который я объяснил выше. Вы можете использовать TimerTask, AsyncTask или обработчики или загрузчики в зависимости от контекста. Это потому, что если UIThread ждет долго, вы знаете, что произойдет: /

Прутвидхар Рао Надунору
источник
0

Это справедливо, несмотря на то, что запланированные мероприятия, например, для ScheduledExecutorService: новые случаи зарезервированного задания не запускаются.

Мы должны ожидать, что у вас есть удобное приложение, в котором администрирование агента выполняет N поручений.

Я не улавливаю это без усилий? Возможно, вам нужно, чтобы ваше приложение не имело возможности отправлять дополнительные задания администрации агента, а тем временем вам нужно дождаться завершения ваших текущих N обязательств.

За исключением случаев, когда вы полностью уверены, что ваши дела в конце концов будут выполнены, вам нужно будет сидеть спокойно в течение определенного перерыва, а после этого просто выйти, оставив бегающие струны.

Если ваши действия предназначены для реагирования на вмешательство, это будет нормально работать.

Еще одна интригующая ситуация - это момент, когда у вас есть ScheduledExecutorService, который разыгрывает действие.

Лучший способ остановить цепочку действий - вызвать shutdown ()

Девлакс
источник