ExecutorService в Java: управление пулом потоков

Создание потока дорогостоящая операция.

Поэтому можно вместо создания новых потоков переиспользовать те которые завершили свою работу.

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

Также он назначает задачи этим потокам в этом пуле (в пуле всё время находятся те же самые потоки, это важно).

Он также предоставляет возможность ставить задачи в очередь до тех пор, пока не появится свободный поток в пуле, если количество задач превышает количество доступных потоков.

Как уже было сказано ExecutorService создает некоторый пул потоков.

Example

Например:

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

То есть потоки в пуле потоков не создаются заново, а переиспользуются.

Search Icon

Это частно может быть полезно в клиент-серверных программах.

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

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

Пример программы:

import java.io.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class ServiceExecutorExample { //к этой переменной добавляется //в потоках SomeThread static volatile int j; public static void main(String[] args) { ExecutorService exeServ = //одновременно будет работать //только 10 потоков Executors.newFixedThreadPool(10); for (int i = 0; i < 1000; i++) { //в этом цикле 10 потоков будет //переполнены 100 раз. //Запустить поток в ExecutorService. exeServ.submit(new SomeThread()); //Если бы мы запускали здесь потоки //стандартным образом то создалось //бы 1000 потоков. } try { Thread.sleep(1000); } catch (InterruptedException e){} //завершить ExecutorService exeServ.shutdown(); System.out.println("Counter: " + j); } static class SomeThread implements Runnable{ @Override public void run(){ try{ for (int i = 0; i < 100; i++){ j++; } } catch(Exception e){} } } }

Вывод:

То есть ExecutorService нам нужен чтобы некоторое количество потоков, некоторое продолжительное время переиспользовалось для выполнения каких-либо задач.

ReadWriteLock в Java

Узнайте, как ReadWriteLock в Java разделяет блокировки на чтение и запись. Оптимизируйте многопоточный доступ к ресурсам с примерами кода и объяснениями.
Time to read: 15

ThreadLocal в Java: переменные потока

ThreadLocal в Java: создавайте потокобезопасные переменные, уникальные для каждого потока. Пример использования и работа с потоконебезопасными объектами.
Time to read: 16

Fork/Join framework в Java

Fork/Join Framework в Java: разбивайте задачи на подзадачи для параллельного выполнения. Пример использования RecursiveTask и оптимизация многопоточных вычислений.
Time to read: 18