Этот пост был первоначально опубликован в Этот пост был первоначально опубликован в
В своем предыдущем блоге я описал основы многопоточности в Java. Нажмите здесь, чтобы прочитать этот блог.
В предыдущем блоге рассказывалось о том, как создавать потоки, расширяя класс потоков и реализуя управляемый интерфейс.
Эта статья будет посвящена 2 темам.
- Создание потоков путем реализации вызываемого интерфейса
- Использование платформы Executor на Java
Чтобы создать фрагмент кода, который можно запускать в потоке, мы создаем класс, а затем реализуем Вызываемый Интерфейс. Задача, выполняемая этим фрагментом кода, должна быть помещена в функцию call() . В приведенном ниже коде вы можете видеть, что Вызываемая задача – это класс, который реализует Вызываемую Интерфейс, и задача суммирования чисел от 0 до 4 выполняется в функции.
import java.util.concurrent.Callable; class CallableTask implements Callable{ @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 5; i++) { sum += i; } return sum; } }
В приведенном выше коде вы могли бы заметить, что Вызываемый имеет параметр Целое число . Это показывает, что возвращаемый тип этого вызываемого объекта будет целочисленным. Также видно, что функция вызова возвращает тип Целое число .
Создание потоков и их запуск
Приведенный ниже код показывает, как создавать потоки, а затем запускать их.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableInterfaceDemo {
public static void main(String[] args) {
FutureTask[] futureList = new FutureTask[5];
for (int i = 0; i <= 4; i++) {
Callable callable = new CallableTask();
futureList[i] = new FutureTask(callable);
Thread t = new Thread(futureList[i]);
t.start();
}
for (int i = 0; i <= 4; i++) {
FutureTask result = futureList[i];
try {
System.out.println("Future Task" + i + ":" + result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
Чтобы создать поток, сначала нам нужно создать экземпляр Вызываемой задачи , которая реализует Вызываемую Интерфейс, как показано на
Callablecallable = new CallableTask();
Затем нам нужно создать экземпляр FutureTask класс и передать экземпляр вызываемого задача в качестве аргумента, как показано в
futureList[i] = new FutureTask(callable);
Затем, чтобы создать поток, мы создаем экземпляр класса Thread и передаем экземпляр класса FutureTask в качестве аргумента, как показано в
Thread t = new Thread(futureList[i]);
Наконец, поток запускается с помощью функции start() .
Получение результата из потока
В случае вызываемых объектов поток может фактически возвращать значение . Чтобы получить это значение, мы можем вызвать функцию get() на экземпляре FutureTask . В нашем коде возвращаемое значение потока представляет собой сумму чисел от 0 до 4.
Это показано в приведенном ниже фрагменте кода
FutureTaskresult = futureList[i]; try { System.out.println("Future Task" + i + ":" + result.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
Также поток может выдавать Исключение , которое может быть обработано с помощью блоков try catch .
Полный Код
Вот полный код, обсуждавшийся до сих пор
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; class CallableTask implements Callable{ @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 5; i++) { sum += i; } return sum; } } public class CallableInterfaceDemo { public static void main(String[] args) { FutureTask [] futureList = new FutureTask[5]; for (int i = 0; i <= 4; i++) { Callable callable = new CallableTask(); futureList[i] = new FutureTask (callable); Thread t = new Thread(futureList[i]); t.start(); } for (int i = 0; i <= 4; i++) { FutureTask result = futureList[i]; try { System.out.println("Future Task" + i + ":" + result.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }
Создание потока на лету каждый раз требует больших ресурсов. Одной из хороших альтернатив для этого является уже настроенное несколько потоков, а затем распределение наших задач по этим потокам. Вот где класс Executors и ExecutorService очень полезны.
На приведенном выше изображении показан пул потоков с 4 потоками. Всякий раз, когда мы хотим выполнить какую-либо задачу, мы можем назначить ее этим потокам. Как только задача будет выполнена, поток будет освобожден для выполнения других задач.
Как использовать фреймворк исполнителя
Вот код, который использует фреймворк исполнителя.
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; class Worker implements Callable{ @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 5; i++) { sum += i; } return sum; } } public class ExecutorDemo { public static void main(String[] args) { ExecutorService executors = Executors.newFixedThreadPool(4); Future [] futures = new Future[5]; Callable w = new Worker(); try { for (int i = 0; i < 5; i++) { Future future = executors.submit(w); futures[i] = future; } for (int i = 0; i < futures.length; i++) { try { System.out.println("Result from Future " + i + ":" + futures[i].get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } finally { executors.shutdown(); } } }
Сначала мы создаем Рабочий Класс, который реализует вызываемый и выполняет нужную нам задачу.
Далее нам нужно создать ExecutorService .
Класс Исполнители имеет несколько реализаций ExecutorService .
Давайте используем класс Исполнители для создания фиксированного пула потоков размером 4. Это делается следующим образом
ExecutorService executors = Executors.newFixedThreadPool(4);
Далее нам нужно отправить нашу задачу в службу исполнителя. Это делается с помощью следующей строки кода
Futurefuture = executors.submit(w);
При отправке задачи мы получаем экземпляр объекта Future . Будущий объект – это то, что будет хранить результат выполнения задачи.
Получение результата потока
Чтобы получить результат каждой задачи, мы можем вызвать метод get() экземпляра Future . Это показано в приведенном ниже фрагменте кода.
try {
System.out.println("Result from Future " + i + ":" + futures[i].get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Поток также может выдавать исключение, которое можно обработать с помощью try catch .
Некоторые возможные сценарии фиксированного пула потоков
- В этом примере мы создали фиксированный пул потоков размером 4
- Если мы отправим в общей сложности 3 задачи в этот ExecutorService, то все 3 задачи будут назначены пулу потоков, и они начнут выполняться.
- Если мы отправим 4 задачи в этот ExecutorService, то снова все 4 задачи будут назначены пулу потоков, и они начнут выполняться.
- Теперь, если мы отправим 5 задач в этот пул потоков. Только 4 задачи будут назначены пулу потоков. Это связано с тем, что размер пула потоков равен 4. 5-я задача будет назначена только тогда, когда один из потоков в пуле будет освобожден
Завершение работы службы исполнителя
Службу EXECUTOR необходимо отключить, когда потоки больше не нужны. Это гарантирует, что JVM не потребляет дополнительных ресурсов.
Службу Executor можно отключить с помощью следующей команды
executors.shutdown();
Видно, что это завершение работы помещается в блок finally . Это делается для того, чтобы завершение работы всегда выполнялось в конце кода, даже если возникло какое-либо исключение.
Если завершение работы выполнено неправильно, то в случае возникновения какого-либо исключения служба ExecutorService все равно будет запущена и будет потреблять дополнительные ресурсы JVM.
Весь код, обсуждаемый в этой статье, можно найти в этом репозитории git
Теперь вы знаете следующие понятия
- Использование вызываемого интерфейса
- Использование платформы Java Executor для многопоточности.
В своих будущих статьях я буду освещать больше тем, касающихся многопоточности
Не стесняйтесь связаться со мной в LinkedIn или подписаться на меня в Twitter.
Если вам понравился этот пост, вы можете зайти на мой сайт https://adityasridhar.com для других подобных сообщений
Оригинал: “https://dev.to/adityasridhar/how-to-use-java-executor-framework-for-multithreading-30oc”