Руководство по прохождению собеседования по Java (руководство по изучению Java, добро пожаловать в Star, будет продолжать улучшаться, приветствуются предложения и рекомендации):GitHub.com/snail Climb/…
Рекомендуем качественные исторические статьи:
Столбец руководства по программированию с параллелизмом на Java
Классическая базовая теория распределенных систем
Вероятно, самое красивое подробное объяснение управления транзакциями Spring.
Достаточно прочитать этот вопрос о виртуальной машине Java (jvm) в интервью
Интеллект-карта этого раздела:
Исходный файл ментальной карты + программное обеспечение ментальной карты Подпишитесь на общедоступную учетную запись WeChat:«Руководство по прохождению собеседования по Java»Ключевое слово ответа:«Многопоточность Java»Получите это бесплатно.
Преимущество использования пула потоков
Пул потоковПредоставляет способ ограничения ресурсов и управления ими (включая выполнение задачи). каждыйПул потоковТакже ведется некоторая основная статистика, например, количество выполненных задач. Вот цитата из "Искусство параллельного программирования на Java"Преимущества использования пулов потоков:
- Сокращение потребления ресурсов. Сократите стоимость создания и уничтожения потоков за счет повторного использования уже созданных потоков.
- Улучшить отзывчивость. При поступлении задачи она может выполняться немедленно, не дожидаясь создания потока.
- Улучшить управляемость потоками. Потоки - это дефицитные ресурсы. Если они создаются без ограничений, это не только потребляет системные ресурсы, но и снижает стабильность системы. Использование пулов потоков можно использовать для унифицированного распределения, настройки и мониторинга.
Два фреймворка Executor
2.1 Введение
Фреймворк Executor был введен после Java 5. После Java 5 запуск потоков через Executor лучше, чем с использованием метода запуска Thread.Помимо того, что он проще в управлении и более эффективен (реализован с пулами потоков, экономит накладные расходы), есть также ключевые моменты. : помогает избежать этой проблемы побега.
Добавлено: этот экран ссылается на другие потоки, содержащие ссылку на объект до возврата конструктора.Вызов методов для объектов, которые еще не были полностью созданы, может вызвать загадочные ошибки.
2.2 Структура платформы исполнителя (в основном состоит из трех частей)
1 задание.
что нужно сделать для выполнения заданияРаботающий интерфейсилиВызываемый интерфейс.Работающий интерфейсилиВызываемый интерфейсКлассы реализации могут бытьThreadPoolExecutorилиScheduledThreadPoolExecutorвоплощать в жизнь.
Разница между ними:
Работающий интерфейсне будет возвращать результаты, ноВызываемый интерфейсрезультаты можно вернуть. Представлено позжеКласс исполнителейНекоторые из методов будут представлены для взаимного преобразования двух.
2 Выполнение заданий
Как показано на рисунке ниже, включая механизм выполнения задачиОсновной интерфейс Исполнитель, и унаследован от интерфейса ExecutorИнтерфейс ExecutorService.ScheduledThreadPoolExecutorиThreadPoolExecutorЭти два ключевых класса реализуютИнтерфейс ExecutorService.
Уведомление:Изучив исходный код ScheduledThreadPoolExecutor, мы обнаружили, что ScheduledThreadPoolExecutor на самом деле наследует ThreadPoolExecutor и реализует ScheduledExecutorService, который, в свою очередь, реализует ExecutorService, как показано на приведенной ниже диаграмме классов.
Описание класса ThreadPoolExecutor:
//AbstractExecutorService实现了ExecutorService接口
public class ThreadPoolExecutor extends AbstractExecutorService
Описание класса ScheduledThreadPoolExecutor:
//ScheduledExecutorService实现了ExecutorService接口
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
3 Результаты асинхронных вычислений
Будущий интерфейсИ класс реализации интерфейса FutureКласс FutureTask. когда мы ставимРаботающий интерфейсилиВызываемый интерфейсКласс реализации отправляет (вызывает метод отправки) вThreadPoolExecutorилиScheduledThreadPoolExecutor, возвращаетОбъект FutureTask.
мы начинаем сAbstractExecutorServiceМетод submit в интерфейсе — это пример просмотра исходного кода:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
Метод newTaskFor, вызванный вышеуказанным методом, возвращает объект FutureTask.
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
2.3 Принципиальная схема использования фреймворка Executor
-
Основной поток сначала создает объект задачи, реализующий интерфейс Runnable или Callable. Примечание:Инструментальный класс Executors может реализовать взаимное преобразование между объектами Runnable и Callable. (Executors.callable(выполняемая задача) или Executors.callable(выполняемая задача, результат объекта)).
-
Затем вы можете напрямую передать созданный объект Runnable в ExecutorService для выполнения.(ExecutorService.execute(Выполняемая команда)); или вы можете отправить объекты Runnable или Callable в ExecutorService для выполнения (ExecutorService.submit(Выполняемая задача) или ExecutorService.submit(Вызываемая задача)).
В чем разница между выполнением метода execute() и метода submit()? 1)Метод execute() используется для отправки задач, не требующих возвращаемого значения, поэтому невозможно определить, успешно ли выполняется задача пулом потоков или нет; 2)Метод submit() используется для отправки задач, требующих возвращаемого значения. Пул потоков вернет объект типа future, с помощью которого можно использовать объект future, чтобы определить, успешно ли выполнена задача., а возвращаемое значение можно получить с помощью метода get() в будущем.Метод get() будет блокировать текущий поток до тех пор, пока задача не будет завершена, а метод get(long timeout, TimeUnit unit) заблокирует текущий поток и вернуться сразу после определенного периода времени.Иногда задание может быть не выполнено.
-
Если вы выполните ExecutorService.submit(...), ExecutorService вернет объект, реализующий интерфейс Future.(Мы только что упомянули разницу между выполнением метода execute() и метода submit(). До сих пор в JDK возвращался объект FutureTask). Поскольку FutureTask реализует Runnable, программисты также могут создавать FutureTask и передавать их ExecutorService для выполнения.
-
Наконец, основной поток может выполнить метод FutureTask.get(), чтобы дождаться завершения задачи. Основной поток также может выполнить FutureTask.cancel(boolean mayInterruptIfRunning), чтобы отменить выполнение этой задачи.
Три детали ThreadPoolExecutor
Класс реализации пула потоков ThreadPoolExecutor является основным классом среды Executor. Давайте рассмотрим еще четыре важных атрибута этого класса.
3.1 Еще четыре важных свойства класса ThreadPoolExecutor
3.2 Четыре конструктора в классе ThreadPoolExecutor
Давайте посмотрим на самый длинный, а остальные три генерируются на основе этого метода построения (метод построения с некоторыми параметрами по умолчанию)
/**
* 用给定的初始参数创建一个新的ThreadPoolExecutor。
* @param keepAliveTime 当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,
*核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
* @param unit keepAliveTime参数的时间单位
* @param workQueue 等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
*
* @param threadFactory 执行者创建新线程时使用的工厂
* @param handler RejectedExecutionHandler类型的变量,表示线程池的饱和策略。
* 如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。
* 线程池提供了4种策略:
1.AbortPolicy:直接抛出异常,这是默认策略;
2.CallerRunsPolicy:用调用者所在的线程来执行任务;
3.DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4.DiscardPolicy:直接丢弃任务;
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
3.3 Как создать ThreadPoolExecutor
Способ 1: Реализован методом построения (официальная документация по API не рекомендуется, поэтому рекомендуется использовать второй способ)
Способ 2: Реализован через класс инструментов Executors фреймворка Executor. Мы можем создать три типа ThreadPoolExecutor:- FixedThreadPool
- SingleThreadExecutor
- CachedThreadPool
Методы в соответствующем инструментальном классе Executors показаны на рисунке:
3.4 Подробное объяснение FixedThreadPool
FixedThreadPool известен как пул потоков с фиксированным числом повторно используемых потоков. Взгляните на соответствующую реализацию через соответствующий исходный код в классе Executors:
/**
* 创建一个可重用固定数量线程的线程池
*在任何时候至多有n个线程处于活动状态
*如果在所有线程处于活动状态时提交其他任务,则它们将在队列中等待,
*直到线程可用。 如果任何线程在关闭之前的执行期间由于失败而终止,
*如果需要执行后续任务,则一个新的线程将取代它。池中的线程将一直存在
*知道调用shutdown方法
* @param nThreads 线程池中的线程数
* @param threadFactory 创建新线程时使用的factory
* @return 新创建的线程池
* @throws NullPointerException 如果threadFactory为null
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
Существует также метод реализации FixedThreadPool, аналогичный описанному выше, поэтому я не буду здесь подробно останавливаться:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Из приведенного выше исходного кода видно, что для corePoolSize и maxPoolSize только что созданного FixedThreadPool установлено значение nThreads.Схематическая диаграмма метода execute() FixedThreadPool (источник изображения: «Искусство параллельного программирования на Java»):
Картинка выше поясняет:
- Если количество запущенных в данный момент потоков меньше, чем corePoolSize, создайте новый поток для выполнения задачи;
- После того, как количество запущенных в данный момент потоков сравняется с corePoolSize, добавьте задачу в LinkedBlockingQueue;
- После того, как поток выполнит задачу в 1, он будет повторно получать задачу из LinkedBlockingQueue в цикле для выполнения;
FixedThreadPool использует неограниченную очередь LinkedBlockingQueue (емкость очереди — Intger.MAX_VALUE) в качестве рабочей очереди пула потоков, что будет иметь следующие последствия для пула потоков:
- Когда количество потоков в пуле потоков достигает corePoolSize, новые задачи будут ожидать в неограниченной очереди, поэтому количество потоков в пуле потоков не будет превышать corePoolSize;
- из-за 1, maxPoolSize был бы недопустимым параметром при использовании неограниченной очереди;
- Из-за 1 и 2 параметр keepAliveTime будет недопустимым при использовании неограниченных очередей;
- Работающий FixedThreadPool (без выполнения методов shutdown() или shutdownNow()) не отклоняет задачи.
3.5 Подробное объяснение SingleThreadExecutor
SingleThreadExecutor — это Executor, использующий один рабочий поток. Посмотрите нижеРеализация SingleThreadExecutor:
/**
*创建使用单个worker线程运行无界队列的Executor
*并使用提供的ThreadFactory在需要时创建新线程
*
* @param threadFactory 创建新线程时使用的factory
*
* @return 新创建的单线程Executor
* @throws NullPointerException 如果ThreadFactory为空
*/
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
Из приведенного выше исходного кода видно, что для corePoolSize и maxPoolSize только что созданного SingleThreadExecutor установлено значение 1. Остальные параметры такие же, как у FixedThreadPool. SingleThreadExecutor использует неограниченную очередь LinkedBlockingQueue в качестве рабочей очереди пула потоков (емкость очереди — Intger.MAX_VALUE). SingleThreadExecutor, использующий неограниченную очередь в качестве рабочей очереди пула потоков, окажет такое же влияние на пул потоков, как FixedThreadPool.
Схематическая диаграмма работы SingleThreadExecutor (источник изображения: «Искусство параллельного программирования на Java»):
Описание выше;
- Если количество запущенных в данный момент потоков меньше, чем corePoolSize, создайте новый поток для выполнения задачи;
- После того, как в текущем пуле потоков появится работающий поток, добавьте задачу в LinkedBlockingQueue.
- После того, как поток выполнит задачу в 1, он будет повторно получать задачу из LinkedBlockingQueue в цикле для выполнения;
3.6 Подробное объяснение CachedThreadPool
CachedThreadPool — это пул потоков, который создает новые потоки по мере необходимости. Давайте посмотрим на реализацию CachedThreadPool через исходный код:
/**
* 创建一个线程池,根据需要创建新线程,但会在先前构建的线程可用时重用它,
*并在需要时使用提供的ThreadFactory创建新线程。
* @param threadFactory 创建新线程使用的factory
* @return 新创建的线程池
* @throws NullPointerException 如果threadFactory为空
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CorePoolSize CachedThreadPool устанавливается пустым (0), а MaximumPoolSize устанавливается равным Integer.MAX.VALUE, то есть он неограничен, что означает, что если скорость отправки задач основным потоком выше, чем скорость потоков, обрабатывающих задачи в максимальном пуле, CachedThreadPool будет продолжать создавать новые потоки. В крайних случаях это может привести к истощению ресурсов ЦП и памяти.
Схематическая диаграмма выполнения метода execute() CachedThreadPool (источник изображения: «Искусство параллельного программирования на Java»):
Картинка выше поясняет:
- Сначала выполните SynchronousQueue.offer (запускаемая задача). Если бездействующий поток в текущем максимальном пуле выполняет SynchronousQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS), то основной поток выполняет операцию предложения, и операция опроса, выполняемая бездействующим потоком, успешно спаривается, и основной поток назначает задачу бездействующий поток для выполнения, метод execute() Выполнение завершено, в противном случае выполните следующий шаг 2;
- Если исходный максимальный пул пуст или в нем нет простаивающих потоков, не будет ни одного потока, выполняющего SynchronousQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS). В этом случае произойдет сбой шага 1. В это время CachedThreadPool создаст новый поток для выполнения задачи, и метод execute завершится;
3.7 Пример использования ThreadPoolExecutor
3.7.1 Пример кода
Сначала создайте класс реализации интерфейса Runnable (конечно, это также может быть интерфейс Callable. Мы также упомянули разницу между двумя выше: интерфейс Runnable не возвращает результаты, но интерфейс Callable может возвращать результаты. При введении некоторых методов класса Executors, мы познакомимся с их взаимным преобразованием.)
import java.util.Date;
/**
* 这是一个简单的Runnable类,需要大约5秒钟来执行其任务。
*/
public class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s) {
this.command = s;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
processCommand();
System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return this.command;
}
}
Напишите тестовую программу, здесь мы возьмем FixedThreadPool в качестве примера
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
//创建一个FixedThreadPool对象
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
//创建WorkerThread对象(WorkerThread类实现了Runnable 接口)
Runnable worker = new WorkerThread("" + i);
//执行Runnable
executor.execute(worker);
}
//终止线程池
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
}
}
Пример вывода:
pool-1-thread-5 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-3 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-2 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-4 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-1 Start. Time = Thu May 31 10:22:52 CST 2018
pool-1-thread-4 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-1 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-2 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-5 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-3 End. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-5 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-2 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-1 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-4 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-3 Start. Time = Thu May 31 10:22:57 CST 2018
pool-1-thread-5 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-1 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-2 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-3 End. Time = Thu May 31 10:23:02 CST 2018
pool-1-thread-4 End. Time = Thu May 31 10:23:02 CST 2018
Finished all threads
3.7.2 shutdown() и shutdownNow()
Метод shutdown() указывает, что в Executor было вызвано завершение работы, поэтому в DelayedPool (внутренне используемый классом ScheduledThreadPoolExecutor) не будет добавлено никаких дополнительных задач. Однако задачи, уже отправленные в очередь, будут разрешены для завершения. С другой стороны, метод shutdownNow() пытается завершить текущую задачу, останавливает обработку задач в очереди и возвращает список, ожидающий выполнения. ####3.7.3 isTerminated() и isShutdown() isShutdown() указывает, что исполнитель завершает работу, но не все задачи завершили выполнение. С другой стороны, isShutdown() означает, что все потоки завершили выполнение.
Четыре детали ScheduledThreadPoolExecutor
4.1 Введение
ScheduledThreadPoolExecutor в основном используется для запуска задач после заданной задержки или для периодического выполнения задач.
Очередь задач DelayQueue, используемая ScheduledThreadPoolExecutor, инкапсулирует PriorityQueue, PriorityQueue сортирует задачи в очереди, и задачи с наименьшим временем выполнения будут выполняться первыми (переменная времени ScheduledFutureTask мала и выполняется первой), если время выполнения то же самое. Задача, представленная первой, будет выполнена первой (чем меньше переменная sequenceNumber ScheduledFutureTask, выполняется первой).
Сравнение ScheduledThreadPoolExecutor и Timer:
- Timer чувствителен к изменениям системных часов, ScheduledThreadPoolExecutor — нет;
- Таймер имеет только один поток выполнения, поэтому длительные задачи могут задерживать другие задачи. ScheduledThreadPoolExecutor можно настроить на любое количество потоков. Кроме того, если вы хотите (предоставив ThreadFactory), у вас есть полный контроль над созданными потоками;
- Исключение времени выполнения, созданное в TimerTask, убивает поток, в результате чего таймер умирает :-( ... т. е. запланированная задача больше не будет выполняться. ScheduledThreadExecutor не только перехватывает исключения времени выполнения, но также позволяет вам обрабатывать их при необходимости (через Override метод afterExecute ThreadPoolExecutor) Задача, вызвавшая исключение, будет отменена, но другие задачи продолжат выполняться.
Таким образом, после JDK1.5 у вас нет причин использовать Timer для планирования задач.
Примечание:Quartz — это библиотека планирования задач, написанная на java и предоставленная организацией OpenSymphony с открытым исходным кодом. Большинство людей используют Quartz при разработке реальных проектов, и рекомендуется использовать Quartz. Поскольку Quartz теоретически может планировать десятки тысяч задач одновременно, он обладает широкими возможностями, включая планирование задач, постоянство задач, кластеризацию, подключаемые модули и многое другое.
4.2 Механизм запуска ScheduledThreadPoolExecutor
Выполнение ScheduledThreadPoolExecutor в основном делится на две части:
- При вызове ScheduledThreadPoolExecutorscheduleAtFixedRate()метод илиscheduleWirhFixedDelay()метод, будет сообщать ScheduledThreadPoolExecutorDelayQueueдобавить реализациюRunnableScheduledFuturинтерфейсScheduledFutureTask.
- Потоки в пуле потоков получают ScheduledFutureTask из DelayQueue, а затем выполняют задачу.
ScheduledThreadPoolExecutor внес следующие изменения в ThreadPoolExecutor для реализации периодического выполнения задач:
- использоватьDelayQueueкак очередь задач;
- Способ получения задания разный
- После выполнения периодической задачи добавляется дополнительная обработка
4.3 Действия ScheduledThreadPoolExecutor для выполнения периодических задач
- Поток 1 получает запланированную задачу ScheduledFutureTask с истекшим сроком действия из DelayQueue (DelayQueue.take()). Выполненная задача означает, что время ScheduledFutureTask больше или равно текущему системному времени;
- Поток 1 выполняет эту ScheduledFutureTask;
- Поток 1 изменяет переменную времени ScheduledFutureTask на время, которое будет выполнено в следующий раз;
- Поток 1 помещает ScheduledFutureTask после измененного времени обратно в DelayQueue (DelayQueue.add()).
4.4 Пример использования ScheduledThreadPoolExecutor
-
Создайте простой класс, реализующий интерфейс Runnable (который мы уже реализовали в примере выше).
-
В тестовой программе используется планирование Java, реализованное с помощью ScheduledExecutorService и ScheduledThreadPoolExecutor.
/**
* 使用ScheduledExecutorService和ScheduledThreadPoolExecutor实现的java调度程序示例程序。
*/
public class ScheduledThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
//创建一个ScheduledThreadPoolExecutor对象
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//计划在某段时间后运行
System.out.println("Current Time = "+new Date());
for(int i=0; i<3; i++){
Thread.sleep(1000);
WorkerThread worker = new WorkerThread("do heavy processing");
//创建并执行在给定延迟后启用的单次操作。
scheduledThreadPool.schedule(worker, 10, TimeUnit.SECONDS);
}
//添加一些延迟让调度程序产生一些线程
Thread.sleep(30000);
System.out.println("Current Time = "+new Date());
//关闭线程池
scheduledThreadPool.shutdown();
while(!scheduledThreadPool.isTerminated()){
//等待所有任务完成
}
System.out.println("Finished all threads");
}
}
результат операции:
Current Time = Wed May 30 17:11:16 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:11:27 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:11:28 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:11:29 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:11:32 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:11:33 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:11:34 CST 2018
Current Time = Wed May 30 17:11:49 CST 2018
Finished all threads
4.4.1 Метод ScheduledExecutorService scheduleAtFixedRate (выполняемая команда, длинная начальная задержка, длительный период, единица времени)
Мы можем использовать метод ScheduledExecutorService scheduleAtFixedRate, чтобы запланировать запуск задачи после первоначальной задержки, а затем в течение заданного периода времени.
Период времени начинается с начала первого потока в пуле, поэтому, если вы укажете период в 1 секунду и поток будет работать в течение 5 секунд, следующий поток начнет выполняться, как только завершится выполнение первого рабочего потока.
for (int i = 0; i < 3; i++) {
Thread.sleep(1000);
WorkerThread worker = new WorkerThread("do heavy processing");
// schedule task to execute at fixed rate
scheduledThreadPool.scheduleAtFixedRate(worker, 0, 10,
TimeUnit.SECONDS);
}
Пример вывода:
Current Time = Wed May 30 17:47:09 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:10 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:47:11 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:47:12 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:15 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:47:16 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:47:17 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:20 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:47:21 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:47:22 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:25 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:47:26 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:47:27 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:30 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:47:31 CST 2018
pool-1-thread-5 Start. Time = Wed May 30 17:47:32 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:35 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:47:36 CST 2018
pool-1-thread-5 End. Time = Wed May 30 17:47:37 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:47:40 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:47:41 CST 2018
Current Time = Wed May 30 17:47:42 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:47:45 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:47:46 CST 2018
Finished all threads
Process finished with exit code 0
4.4.2 Метод ScheduledExecutorService scheduleWithFixedDelay (исполняемая команда, long initialDelay, long delay, TimeUnit unit)
Метод ScheduleWithFixedDelay ScheduledExecutorService можно использовать для запуска периодического выполнения с начальной задержкой, а затем выполнения с заданной задержкой. Задержка — это время, необходимое потоку для завершения выполнения.
for (int i = 0; i < 3; i++) {
Thread.sleep(1000);
WorkerThread worker = new WorkerThread("do heavy processing");
scheduledThreadPool.scheduleWithFixedDelay(worker, 0, 1,
TimeUnit.SECONDS);
}
Пример вывода:
Current Time = Wed May 30 17:58:09 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:10 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:11 CST 2018
pool-1-thread-3 Start. Time = Wed May 30 17:58:12 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:15 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:16 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:16 CST 2018
pool-1-thread-3 End. Time = Wed May 30 17:58:17 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:17 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:18 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:21 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:22 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:22 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:23 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:23 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:24 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:27 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:28 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:28 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:29 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:29 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:30 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:33 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:34 CST 2018
pool-1-thread-1 Start. Time = Wed May 30 17:58:34 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:35 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:35 CST 2018
pool-1-thread-4 Start. Time = Wed May 30 17:58:36 CST 2018
pool-1-thread-1 End. Time = Wed May 30 17:58:39 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:40 CST 2018
pool-1-thread-5 Start. Time = Wed May 30 17:58:40 CST 2018
pool-1-thread-4 End. Time = Wed May 30 17:58:41 CST 2018
pool-1-thread-2 Start. Time = Wed May 30 17:58:41 CST 2018
Current Time = Wed May 30 17:58:42 CST 2018
pool-1-thread-5 End. Time = Wed May 30 17:58:45 CST 2018
pool-1-thread-2 End. Time = Wed May 30 17:58:46 CST 2018
Finished all threads
4.4.3 scheduleWithFixedDelay() vs scheduleAtFixedRate()
scheduleAtFixedRate(...) рассматривает задержку как разницу между запуском двух задач (т.е. вызывается периодически) scheduleWithFixedDelay(...) рассматривает задержку как разницу между окончанием одной задачи и началом следующей
scheduleAtFixedRate():Создает и выполняет периодическое действие, которое включается сначала после заданной начальной задержки, а затем через заданный период времени, то есть выполнение начнется после initialDelay, затем initialDelay+period, затем initialDelay+2*period и так далее. Если при выполнении задачи возникает исключение, последующее выполнение подавляется. В противном случае задача будет прекращена только путем отмены или завершения исполнителя. Если задача выполняется дольше своего периода, последующие выполнения могут задерживаться, но не одновременно.scheduleWithFixedDelay() :Создает и выполняет периодические действия, которые активируются сначала после заданной начальной задержки, а затем с заданной задержкой между завершением одного выполнения и началом следующего. Если при выполнении задачи возникает исключение, последующее выполнение подавляется. В противном случае задача просто завершится путем отмены или прекращения выполнения.
Пятое введение в применимые сценарии различных пулов потоков
Фиксированный пул потоков:Он подходит для сценариев приложений, в которых необходимо ограничить текущее количество потоков, чтобы удовлетворить требования управления ресурсами. Подходит для серверов с большой нагрузкой;
Однопотоковый исполнитель:Он подходит для использования в сценариях, где необходимо гарантировать последовательное выполнение задач и отсутствие активности нескольких потоков в любой момент времени.
Кэшедтредпул:Он подходит для небольших программ, выполняющих множество краткосрочных асинхронных задач, или серверов с небольшой нагрузкой;
Запланированный поток пула исполнителей:Он подходит для сценариев приложений, которые требуют выполнения нескольких периодических задач в фоновом режиме и ограничивают количество фоновых потоков для удовлетворения требований управления ресурсами.
СинглThreadScheduledExecutor:Он подходит для сценариев приложений, в которых требуется один фоновый поток для выполнения периодических задач, при этом каждая задача выполняется последовательно.
Шесть резюме
В этом разделе кратко представлены преимущества использования пулов потоков, а затем уделено много времени знакомству с инфраструктурой Executor. Подробно представлены ThreadPoolExecutor и ScheduledThreadPoolExecutor в среде Executor, а использование ScheduledThreadPoolExecutor подробно объясняется на примерах. Для FutureTask я лишь вкратце упомянул об этом, из-за проблем с местом я не стал вникать в его принцип и буду дополнять в следующих статьях. В этой статье вы ознакомитесь только с базовым обзором пула потоков. Здесь не так много мест для подробных объяснений. Позже мы изучим некоторые из наиболее важных точек знаний с помощью исходного кода.
Наконец, через две недели будут экзамены, так что я возьму немного времени, чтобы просто разобраться со школьными экзаменами. Тогда на написание этой многопоточной статьи ушло много времени. Я никогда не знал, с чего начать.
Ссылаться на
Искусство параллельного программирования на Java
Java Scheduler ScheduledExecutorService ScheduledThreadPoolExecutor Example
java.util.concurrent.ScheduledThreadPoolExecutor Example
ThreadPoolExecutor — пример пула потоков Java
Я Snailclimb, новичок, планирующий стать архитектором через 5 лет. Добро пожаловать в мой публичный аккаунт WeChat: "Руководство по прохождению собеседования на Java"(Теплый общедоступный аккаунт WeChat, с нетерпением жду вместе с вами прогресса ~~~ настаивайте на оригинальности, делитесь красивыми текстами и делитесь различными учебными ресурсами Java)
Наконец, поработав некоторое время с сервером Alibaba Cloud, я понял, что Alibaba Cloud действительно хорош, поэтому подал заявку на пост представителя Alibaba Cloud.мой адрес купона.