Рубрики
Без рубрики

Глубокое погружение в Java Executorservice framework – Часть 1

Это подробное руководство, в котором шаг за шагом описывается Java Executorservice, которое будет полезно для людей, которые проходят собеседование, чтобы описать его дизайн или хотят отладить и выяснить основную причину сбоев при выполнении того же самого в рабочей среде. Помечены исполнителями, многопоточностью, java, threadpool.

Вступление:

Привет, ребята! Это мой первый технический блог, который я начинаю вести под своим доменом. Раньше я был заядлым потребителем постов в блогах, но пришло время перейти к моему собственному приятному пространству в Интернете в качестве производителя контента, поскольку я чувствую, что важно делиться своими знаниями с сообществом.

Вот ссылка на оригинальную статью !

В этом сообщении в блоге я хотел бы подробно рассказать о реализации платформы ExecutorService framework, выпущенной вместе с JDK 5. Он используется для запуска управляемых объектов с использованием таких понятий, как ThreadPool и ThreadFactory.

Если вы планируете реализовать какой-то параллелизм в своем проекте, я бы порекомендовал вам использовать фреймворки более высокого уровня, подобные этому, вместо того, чтобы начинать все с нуля, поскольку это гарантирует, что ваша кодовая база меньше подвержена влиянию ситуаций типа datarace во время выполнения и оставит большую часть тяжелой работы по управлению потоки и синхронизация с чем-то более детерминированным

содержание:

  1. Executors.java Фабрика
  2. Интерфейс ExecutorService
  3. Концепция пула потоков

1. Executors.java Фабрика

Первое, что мы делаем, когда используем платформу ExecutorService, – это используем класс Executors factory для возврата экземпляра объекта, который реализует все методы интерфейса ExecutorService. Мы вызываем общедоступные статические методы создания экземпляра threadpool в конструкторе, чтобы вернуть эту конкретную обернутую реализацию ExecutorService. Как только мы получим экземпляр этой службы, мы сможем отправлять задачи в службу и отслеживать ход выполнения каждой из этих задач. Вы можете найти этот класс в разделе package java.util.concurrent . Ниже приведен примерный фрагмент достижения того, что я только что обсуждал:

ExecutorService executorService = Executors.newFixedThreadPool(10);

try{
    executorService.execute(new Runnable() {
        public void run() {
            System.out.println("Asynchronous task");
        }
    });
}

finally {
    executorService.shutdown(); 
}

Различными реализациями пулов потоков, которые могут быть созданы из класса Executors, являются:

  • Фиксированный пул потоков : Создает пул потоков, который повторно использует фиксированное количество потоков, работающих из общей неограниченной очереди. В любой момент не более потоков будут активными задачами обработки. Я подробно объясню, в части 2 серии, двойной щелчок по этой реализации

  • Однопоточный исполнитель : Создает однопоточный исполнитель, который может планировать выполнение команд после заданной задержки или для периодического выполнения. (Обратите внимание, однако, что если этот единственный поток завершается из-за сбоя во время выполнения перед завершением работы, его место займет новый, если это необходимо для выполнения последующих задач.) Задачи гарантированно выполняются последовательно, и в любой момент времени будет активна не более одной задачи. Гарантируется, что возвращаемый исполнитель не будет перенастроен для использования дополнительных потоков.

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

2. Интерфейс ExecutorService

Интерфейс ExecutorService расширяет интерфейс исполнителя, который представляет собой объект, выполняющий отправленные Выполняемые задачи. Этот интерфейс предоставляет способ отделить отправку задачи от механики выполнения каждой задачи, включая детали использования потоков, планирования и т.д. Вместо явного создания потоков обычно используется Executor . Например, вместо вызова нового потока(new(Runnable Task())).start() для каждой из набора задач вы могли бы использовать. Вот конкретный класс, который реализует этот интерфейс:

class ThreadPerTaskExecutor implements Executor {
   public void execute(Runnable r) {
     new Thread(r).start();
   }
}

Пример фрагмента его вызова:

Executor executor = new ThreadPerTaskExecutor();
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());

Некоторые из основных методов интерфейса ExecutorService, которые должны быть реализованы классами реализации, следующие:

  • void shutdown(); : Инициирует упорядоченное завершение работы, при котором выполняются ранее отправленные задачи, но новые задачи приниматься не будут. Вызов не имеет никакого дополнительного эффекта, если он уже выключен.

  • List shutdownNow(); : Пытается остановить все активно выполняемые задачи, останавливает обработку ожидающих задач и возвращает список задач, ожидавших выполнения.

  • логическое значение isShutdown(); : Возвращает true если этот исполнитель был закрыт.

  • логическое значение истерминировано(); : Возвращает true если все задачи завершены после завершения работы.

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

  • Future submit(Вызываемая задача); : Отправляет задачу, возвращающую значение, на выполнение и возвращает Будущее, представляющее ожидающие результаты задачи. Метод Future get вернет результат задачи после успешного завершения.

  • Future submit(Выполняемая задача, результат T); : Отправляет выполняемую задачу на выполнение и возвращает Будущее, представляющее эту задачу. Метод Future get вернет заданный результат после успешного завершения.

  • Список<Будущее> Вызовите все(Коллекция расширяет вызываемые> задачи) вызывает исключение InterruptedException; : Выполняет заданные задачи, возвращая список фьючерсов с сохранением их статуса и результатов, когда все завершены. Future#isDone равно true для каждого элемента возвращаемого списка. Обратите внимание, что завершенная задача могла завершиться либо обычным образом, либо путем создания исключения. Результаты этого метода не определены, если данная коллекция изменяется во время выполнения этой операции.

3. Исполнитель пула потоков

Реализация ExecutorService , которая выполняет каждую отправленную задачу, используя один из, возможно, нескольких объединенных потоков, обычно настраиваемых с использованием методов Executors factory. Пулы потоков решают две разные проблемы: они обычно обеспечивают повышенную производительность при выполнении большого количества асинхронных задач из-за снижения накладных расходов на вызов каждой задачи, и они предоставляют средства ограничения и управления ресурсами, включая потоки, потребляемые при выполнении набора задач. Каждый ThreadPoolExecutor также ведет некоторую базовую статистику, такую как количество выполненных задач.

Threadpoolexecutor расширяет в AbstractExecutorService , который снова реализует интерфейс ExecutorService .

Построение ThreadPoolExecutor требует нескольких обязательных параметров для правильной инициализации с использованием правильной стратегии:

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

Возможно, вам интересно, что такое corePoolSize , maximumPoolSize и “workQueue” делают в этом контексте. Параметр corePoolSize сообщает ThreadPoolExecutor инициализировать набор потоков по умолчанию, готовых принять следующую отправленную задачу. workQueue представляет собой структуру данных реализации BlockingQueue для постановки в очередь запросов, превышающих ограничение corePoolSize . Как только corePoolSize достигнут, он начинает опрашивать/принимать эту очередь для задач, а затем планировать один из существующих потоков для выполнения задачи. Таким образом, исполнителю пула потоков удается эффективно обрабатывать ресурсы потоков

Заключительная записка

Эта статья является частью 1 из серии 3 частей , которую я намерен опубликовать. Это дает хорошее представление о структуре реализации платформы ExecutorService и некоторых основных идеях, которые делают эту платформу простой и эффективной в использовании.

Если у вас есть какие-либо вопросы или конструктивная критика, оставьте их в комментариях ниже!

Оригинал: “https://dev.to/suhaspbharadwaj/deep-dive-into-java-executor-service-framework—part-1-2ikd”