Java Concurrent: Пул потоков

Написано 14 Ноябрь, 2013 в категории Кладовая

ExecutorService - это интерфейс для пула потоков. Реализации интерфейса java.util.concurrent.ExecutorService позволяют запускать параллельное выполнение задач.
Что вообще такое "пул потоков"? Говоря абстрактно, это список "исполнителей", готовых выполнить задачу, которую вы им передадите, в отдельном потоке. Выполнение каждой задачи занимает какое-то время
и если в пуле не окажется свободных "исполнителей", то ваша задача станет в очередь.

Пример:

1
2
3
4
5
6
7
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});
executorService.shutdown();

В консоль будет выведена строка: "Asynchronous task".

Разберем программу построчно:
1 строка: создаем экземпляр пула потоков типа FixedThread. При создании этого пула в нем создается 10 потоков, которые будут запускать передаваемые пулу задачи.
2 строка: выполняем задачу по выводу строки "Asynchronous task". Метод execute() передает задачу одному из свободных потоков в пуле, поэтому строка будет выведена только один раз.
7 строка: останавливаем все потоки пула, но очень вежливо, т.е. каждый поток в пуле останавливается как только завершит свою задачу.

Если вы решите использовать данный пример как основу для написания своей программы, то Вы скорее всего столкнетесь с одной неожиданностью. В данной программе задача, которую выполняет ExcecutorService настолько короткая, что успевает выполниться до того как в основном потоке будет выполнена остановка пула (7 строка), но в "реальных" программах задачи как правило более функциональные и выполняются дольше, поэтому может возникнуть ситуация когда задача еще не окончена, а мы уже закрываем пул. Выходом из этой ситуации может быть использование вместо метода execute(Runnable), который ничего не возвращает, метода submit(Runnable), который возвращает объект Future. Этот объект позволяет отслеживать статус выполнения задачи потоком.

Как же создать пул потоков? Все очень легко и просто! Разработчики позаботились о нас и сделали фабрику Executors для создания пула потоков того вида, который нам требуется.
Вызывайте
1. Executors.newSingleThreadExecutor() - если нужен пул из одного потока
2. Executors.newFixedThreadPool(10) - если нужен пул строго из 10 потоков
3. Executors.newScheduledThreadPool(10) - если нужно создать пул потоков по расписанию
4. Executors.newCachedThreadPool() - это пул потоков с максимальным количество потоков Integer.MAX_VALUE. Потоки имеют время жизни 1 минуту в состоянии простоя.