Полезные материалы по всему, что может быть полезно разработчику на Java.
Cтек – участок памяти, тесно связанный с потоками. У каждого потока есть свой стек, которые хранит локальные переменные, параметры методов и стек вызовов. Переменная, хранящаяся в стеке одного потока, не видна для другого.
Куча – общий участок памяти, который делится между всеми потоками. Объекты, неважно локальные или любого другого уровня, создаются в куче. Для улучшения производительности, поток обычно кэширует значения из кучи в свой стек, в этом случае для того, чтобы указать потоку, что переменную следует читать из кучи используется ключевое слово volatile.N-процессорной системе для рабочей очереди, которая будет выполнять исключительно задачи с ограничением по скорости вычислений, можно достигнуть максимального использования CPU с пулом потоков, в котором содержится N или N+1 поток. Для задач, которые могут ждать осуществления I/O (ввода - вывода) - например, задачи, считывающей HTTP-запрос из сокета – может понадобиться увеличение размера пула свыше количества доступных процессоров, потому, что не все потоки будут работать все время. Используя профилирование, можно оценить отношение времени ожидания (WT) ко времени обработки (ST) для типичного запроса. Если назвать это соотношение WT/ST, то для N-процессорной системе понадобится примерно N*(1 + WT/ST) потоков для полной загруженности процессоров.
Использование процессора – не единственный фактор, важный при настройке размера пула потоков. По мере возрастания пула потоков, можно столкнуться с ограничениями планировщика, доступной памяти, или других системных ресурсов, таких, как количество сокетов, дескрипторы открытого файла, или каналы связи базы данных. Java был реализован механизм пула потоков (thread pool), который создаётся во время запуска приложения и в дальнейшем потоки для обработки запросов берутся и переиспользуются уже из него. Таким образом, появляется возможность не терять потоки, сбалансировать приложение по количеству потоков и частоте их создания.
Начиная с Java 1.5 Java API предоставляет фреймворк Executor, который позволяет создавать различные типы пула потоков:
• Executor - упрощенный интерфейс пула, содержит один метод для передачи задачи на выполнение;
• ExecutorService - расширенный интерфейс пула, с возможностью завершения всех потоков;
• AbstractExecutorService - базовый класс пула, реализующий интерфейс ExecutorService;
• Executors - фабрика объектов связанных с пулом потоков, в том числе позволяет создать основные типы пулов;
• ThreadPoolExecutor - пул потоков с гибкой настройкой, может служить базовым классом для нестандартных пулов;
• ForkJoinPool - пул для выполнения задач типа ForkJoinTask;
... и другие.
Методы Executors для создания пулов:
• newCachedThreadPool() - если есть свободный поток, то задача выполняется в нем, иначе добавляется новый поток в пул. Потоки не используемые больше минуты завершаются и удалются и кэша. Размер пула неограничен. Предназначен для выполнения множество небольших асинхронных задач;
• newCachedThreadPool(ThreadFactory threadFactory) - аналогично предыдущему, но с собственной фабрикой потоков;
• newFixedThreadPool(int nThreads) - создает пул на указанное число потоков. Если новые задачи добавлены, когда все потоки активны, то они будут сохранены в очереди для выполнения позже. Если один из потоков завершился из-за ошибки, на его место будет запущен другой поток. Потоки живут до тех пор, пока пул не будет закрыт явно методом shutdown().
• newFixedThreadPool(int nThreads, ThreadFactory threadFactory) - аналогично предыдущему, но с собственной фабрикой потоков;
• newSingleThreadScheduledExecutor() - однопотоковый пул с возможностью выполнять задачу через указанное время или выполнять периодически. Если поток был завершен из-за каких-либо ошибок, то для выполнения следующей задачи будет создан новый поток.
• newSingleThreadScheduledExecutor(ThreadFactory threadFactory) - аналогично предыдущему, но с собственной фабрикой потоков;
• newScheduledThreadPool(int corePoolSize) - пул для выполнения задач через указанное время или переодически;
• newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) - аналогично предыдущему, но с собственной фабрикой потоков;
• unconfigurableExecutorService(ExecutorService executor) - обертка на пул, запрещающая изменять его конфигурацию;Java реализован с использованием внутреннего флага, известного как статус прерывания. Прерывание потока вызовом Thread.interrupt() устанавливает этот флаг. Методы Thread.interrupted() и isInterrupted() позволяют проверить, является ли поток прерванным.
Когда прерванный поток проверяет статус прерывания, вызывая статический метод Thread.interrupted(), статус прерывания сбрасывается.
Нестатический метод isInterrupted() используется одним потоком для проверки статуса прерывания у другого потока, не изменяя флаг прерывания.